#include <QtCore/qendian.h>
#include <QVector>
#include <QDebug>
#include "utils.h"
#include "wavfile.h"
struct chunk
{
char id[4];
quint32 size;
};
struct RIFFHeader
{
chunk descriptor;
char type[4];
};
struct WAVEHeader
{
chunk descriptor;
quint16 audioFormat;
quint16 numChannels;
quint32 sampleRate;
quint32 byteRate;
quint16 blockAlign;
quint16 bitsPerSample;
};
struct DATAHeader
{
chunk descriptor;
};
struct CombinedHeader
{
RIFFHeader riff;
WAVEHeader wave;
};
WavFile::WavFile(QObject *parent)
: QFile(parent)
, m_headerLength(0)
{
}
bool WavFile::open(const QString &fileName)
{
close();
setFileName(fileName);
return QFile::open(QIODevice::ReadOnly) && readHeader();
}
const QAudioFormat &WavFile::fileFormat() const
{
return m_fileFormat;
}
qint64 WavFile::headerLength() const
{
return m_headerLength;
}
bool WavFile::readHeader()
{
seek(0);
CombinedHeader header;
bool result = read(reinterpret_cast<char *>(&header), sizeof(CombinedHeader)) == sizeof(CombinedHeader);
if (result) {
if ((memcmp(&header.riff.descriptor.id, "RIFF", 4) == 0
|| memcmp(&header.riff.descriptor.id, "RIFX", 4) == 0)
&& memcmp(&header.riff.type, "WAVE", 4) == 0
&& memcmp(&header.wave.descriptor.id, "fmt ", 4) == 0
&& (header.wave.audioFormat == 1 || header.wave.audioFormat == 0)) {
DATAHeader dataHeader;
if (qFromLittleEndian<quint32>(header.wave.descriptor.size) > sizeof(WAVEHeader)) {
quint16 extraFormatBytes;
if (peek((char*)&extraFormatBytes, sizeof(quint16)) != sizeof(quint16))
return false;
const qint64 throwAwayBytes = sizeof(quint16) + qFromLittleEndian<quint16>(extraFormatBytes);
if (read(throwAwayBytes).size() != throwAwayBytes)
return false;
}
if (read((char*)&dataHeader, sizeof(DATAHeader)) != sizeof(DATAHeader))
return false;
if (memcmp(&header.riff.descriptor.id, "RIFF", 4) == 0)
m_fileFormat.setByteOrder(QAudioFormat::LittleEndian);
else
m_fileFormat.setByteOrder(QAudioFormat::BigEndian);
int bps = qFromLittleEndian<quint16>(header.wave.bitsPerSample);
m_fileFormat.setChannels(qFromLittleEndian<quint16>(header.wave.numChannels));
m_fileFormat.setCodec("audio/pcm");
m_fileFormat.setFrequency(qFromLittleEndian<quint32>(header.wave.sampleRate));
m_fileFormat.setSampleSize(qFromLittleEndian<quint16>(header.wave.bitsPerSample));
m_fileFormat.setSampleType(bps == 8 ? QAudioFormat::UnSignedInt : QAudioFormat::SignedInt);
} else {
result = false;
}
}
m_headerLength = pos();
return result;
}