https://download.csdn.net/download/yulinxx/13507147
wav文件读取类 WaveFile
#ifndef WAVEFILE_H
#define WAVEFILE_H
#include <thread>
#include <mutex>
#include <string>
using namespace std;
class WaveFile
{
public:
struct waveHead
{
char sign[4]; // "RIFF"标志 4
unsigned long int flength; // 文件长度 8
char wavesign[4]; // "WAVE"标志 12
char fmtsign[4]; // "fmt"标志 16
unsigned long int unused; // 过渡字节(不定)20
unsigned short formattype; // 格式类别(10H为PCM形式的声音数据) 22
unsigned short channelnum; // 声道数,单声道为1,双声道为2 24
unsigned long int samplerate; // 采样频率(每秒样本数),表示每个通道的播放速度 28
unsigned long int transferrate; // 数据传输速率,每秒字节数
unsigned short int adjustnum; // 每样本字节数,一个数据单位所占的字节/单个采样位深
unsigned short int databitnum; // 每样本位数,调整数*8 36
}head;
public:
WaveFile();
~WaveFile();
public:
bool WavRead(string filename);
bool readWave(string filename);
short* getData();
unsigned short getChannelNum();
unsigned short getBytePerSample();
int getSampleNum(); // 样本数
short* Data = nullptr; //数据块指针
protected:
public:
unsigned long int m_nDataLength = 0; // 采样数据总数
unsigned long int m_nTotalSample = 0; // 采样点数
unsigned long int m_nBitPerSample = 0; // 采样位数
unsigned long int m_nDataNum = 0; // 数据块大小
unsigned short m_nChannelNum = 0;
unsigned short m_bytePerSample = 0;
int m_nSampleNum = 0; // 样本数
std::mutex m_mtx;
};
#endif // WAVEFILE_H
```cpp
#include "wavefile.h"
#include <QDebug>
WaveFile::WaveFile()
{
}
WaveFile::~WaveFile()
{
if (Data)
{
delete Data;
}
}
bool WaveFile::WavRead(string filename)
{
std::thread readThread(&WaveFile::readWave, this, filename);
readThread.detach();
return true;
}
bool WaveFile::readWave(string filename)
{
if (Data)
{
std::unique_lock<std::mutex> locker(m_mtx);
delete Data;
Data = nullptr;
}
FILE *fp = nullptr;
if ((fp = fopen(filename.c_str(), "rb")) == NULL)
return false;
int nSizeH = sizeof(head);
fread(&head, sizeof(head), 1, fp);
char datasign[4];
fread(datasign, 4, 1, fp); // "DATA" 子块数据域大小
fread(&m_nDataLength, 4, 1, fp); // PCM音频数据长度
m_nTotalSample = m_nDataLength / head.adjustnum; // 采样数 = 文件总长 / 采样位深
m_nBitPerSample = head.databitnum / head.channelnum; // 每秒数据大小 / 声道数
m_nDataNum = m_nTotalSample * m_nBitPerSample / 16; // 总采样数 * 每
//
m_bytePerSample = head.databitnum / 8;
m_nSampleNum = m_nDataLength / m_bytePerSample;
m_nChannelNum = head.channelnum;
std::unique_lock<std::mutex> locker(m_mtx);
unsigned char chTemp;
unsigned short nTemp;
//单字节样本值v为无符号整数(0~255),实际样本值应为v-128;多字节样本值本身就是有符号的,可直接使用
if (m_nChannelNum == 1) // 单声道
{
Data = new short[m_nSampleNum];
if (m_bytePerSample == 1)
{
for (int i = 0; i < m_nSampleNum; i++)
{
fread(&chTemp, 1, 1, fp);
Data[i] = chTemp - 128;
}
}
else if (m_bytePerSample == 2)
{
for (int i = 0; i < m_nSampleNum; i++)
{
fread(&nTemp, 2, 1, fp);
Data[i] = (short)nTemp;
}
}
else if (m_bytePerSample == 3)
{
for (int i = 0; i < m_nSampleNum; i++)
{
fread(&chTemp, 1, 1, fp);
fread(&nTemp, 2, 1, fp);
Data[i] = chTemp + nTemp;
}
}
else if (m_bytePerSample == 4)
{
for (int i = 0; i < m_nSampleNum; i++)
{
fread(&nTemp, 2, 1, fp);
Data[i] = (short)nTemp;
}
}
}
else if (m_nChannelNum == 2) // 双声道
{
Data = new short[m_nSampleNum / 2];
if (m_bytePerSample == 1)
{
for (int i = 0; i < m_nSampleNum / 2; i++)
{
fread(&chTemp, 1, 1, fp);
Data[i] = chTemp - 128;
fread(&chTemp, 1, 1, fp);
}
}
else if (m_bytePerSample == 2)
{
for (int i = 0; i < m_nSampleNum / 2; i++)
{
fread(&nTemp, 2, 1, fp);
Data[i] =nTemp;
fread(&nTemp, 2, 1, fp);
}
}
else if (m_bytePerSample == 3)
{
for (int i = 0; i < m_nSampleNum / 2; i++)
{
fread(&chTemp, 1, 1, fp);
fread(&nTemp, 2, 1, fp);
Data[i] = chTemp + nTemp;
fread(&chTemp, 1, 1, fp);
fread(&nTemp, 2, 1, fp);
}
}
else if (m_bytePerSample == 4)
{
for (int i = 0; i < m_nSampleNum / 2; i++)
{
fread(&nTemp, 2, 1, fp);
Data[i] = (short)nTemp;
fread(&nTemp, 2, 1, fp);
}
}
}
fclose(fp);
return true;
}
short* WaveFile::getData()
{
std::unique_lock<std::mutex> locker(m_mtx);
return Data;
}
unsigned short WaveFile::getChannelNum()
{
return m_nChannelNum;
}
unsigned short WaveFile::getBytePerSample()
{
return m_nBitPerSample;
}
int WaveFile::getSampleNum()
{
return m_nSampleNum;
}
使用:
#ifndef WIDGET_H
#define WIDGET_H
#include "WaveFile.h"
#include <QWidget>
class waveWidget : public QWidget
{
Q_OBJECT
public:
explicit waveWidget(QWidget *parent = 0);
~waveWidget();
public:
void setWavFile(QString strFileName);
protected:
virtual void paintEvent(QPaintEvent *event) override;
private:
void drawSingleChannel(QPainter &p, int W, int H);
void drawDoubleChannel(QPainter &p, int W, int H);
// 波形图解析
WaveFile m_Wavefile;
};
#endif // WIDGET_H
#include "waveWidget.h"
#include <QPainter>
#include <QDebug>
#define MIN(x,y) (x)<(y)?(x):(y)
#define MAX(x,y) (x)>(y)?(x):(y)
waveWidget::waveWidget(QWidget *parent) :
QWidget(parent)
{
setWavFile(QString::fromLocal8Bit("E:\\coin.wav"));
}
waveWidget::~waveWidget()
{
//delete ui;
}
void waveWidget::setWavFile(QString strFileName)
{
//m_Wavefile.WavRead(strFileName.toStdString());
m_Wavefile.readWave(string((const char *)strFileName.toLocal8Bit()));
}
void waveWidget::paintEvent(QPaintEvent *e)
{
int W = this->width();
int H = this->height();
QPixmap pix(W, H); //以此为参数创建一个位图变量
QPalette palette;
QColor color = palette.color(QPalette::Window);
pix.fill(color); // 用窗口背景色填充位图背景色
QPainter p(&pix); //以位图为参数创建一个QPainter 对象
QPen pen;
pen.setColor(QColor(0, 110, 0)); //波形图画笔
p.setPen(pen); //设置画笔
{
if (m_Wavefile.getChannelNum() == 1) // 单声道
drawSingleChannel(p, W, H);
else if (m_Wavefile.getChannelNum() == 2) // 双声道
drawDoubleChannel(p, W, H);
}
QPainter painter(this); // 获取当前画布
painter.drawPixmap(0, 0, pix); // 将位图画到画布上,单缓冲技术
}
void waveWidget::drawSingleChannel(QPainter &p, int W, int H)
{
// 将样本的高度映射到所需高度
float A = pow(2.0, 8.0 * m_Wavefile.m_bytePerSample - 1);
int x = 0, y = H / 2;
while (x < W)
{
int min = INT_MAX, max = INT_MAX+1;
for (int j = x * (m_Wavefile.m_nSampleNum / 2 / W); j < (x + 1) * (m_Wavefile.m_nSampleNum / 2 / W); j++)
{
if (j > 0 && j < m_Wavefile.m_nSampleNum)
{
if (m_Wavefile.Data[j] < min)
min = m_Wavefile.Data[j];
if (m_Wavefile.Data[j] > max)
max = m_Wavefile.Data[j];
}
}
qDebug() << y + (max * H / 2.0 / A) << "---" << y + (min * H / 2.0 / A);
p.drawLine(x, y + (max * H / 2.0 / A), x, y + (min * H / 2.0 / A));
x++;
}
return;
}
void waveWidget::drawDoubleChannel(QPainter &p, int W, int H)
{
float A = pow(2.0, 8.0 * m_Wavefile.m_bytePerSample - 1);
int x = 0, y = H / 2;
while (x < W)
{
int min = INT_MAX, max = INT_MIN;
for (int j = x * (m_Wavefile.m_nSampleNum / 2 / W); j < (x + 1) * (m_Wavefile.m_nSampleNum / 2 / W); j++)
{
if (j > 0 && j < m_Wavefile.m_nSampleNum && m_Wavefile.Data[j] < min)
min = m_Wavefile.Data[j];
if (j > 0 && j<m_Wavefile.m_nSampleNum && m_Wavefile.Data[j]>max)
max = m_Wavefile.Data[j];
}
int y1 = y + (max * H / 2.0 / A);
int y2 = y + (min * H / 2.0 / A);
p.drawLine(x, y1, x, y2);
x++;
}
return;
}