summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/multimedia/effects/qwavedecoder_p.cpp127
-rw-r--r--src/multimedia/effects/qwavedecoder_p.h5
2 files changed, 102 insertions, 30 deletions
diff --git a/src/multimedia/effects/qwavedecoder_p.cpp b/src/multimedia/effects/qwavedecoder_p.cpp
index e173aed8d..ad4ecf26b 100644
--- a/src/multimedia/effects/qwavedecoder_p.cpp
+++ b/src/multimedia/effects/qwavedecoder_p.cpp
@@ -50,9 +50,10 @@ QWaveDecoder::QWaveDecoder(QIODevice *s, QObject *parent):
QIODevice(parent),
haveFormat(false),
dataSize(0),
- remaining(0),
source(s),
- state(QWaveDecoder::InitialState)
+ state(QWaveDecoder::InitialState),
+ junkToSkip(0),
+ bigEndian(false)
{
open(QIODevice::ReadOnly | QIODevice::Unbuffered);
@@ -106,7 +107,15 @@ qint64 QWaveDecoder::writeData(const char *data, qint64 len)
void QWaveDecoder::handleData()
{
- bool valid = true;
+ // As a special "state", if we have junk to skip, we do
+ if (junkToSkip > 0) {
+ discardBytes(junkToSkip); // this also updates junkToSkip
+
+ // If we couldn't skip all the junk, return
+ if (junkToSkip > 0)
+ return;
+ }
+
if (state == QWaveDecoder::InitialState) {
if (source->bytesAvailable() < qint64(sizeof(RIFFHeader)))
return;
@@ -114,7 +123,8 @@ void QWaveDecoder::handleData()
RIFFHeader riff;
source->read(reinterpret_cast<char *>(&riff), sizeof(RIFFHeader));
- if (qstrncmp(riff.descriptor.id, "RIFF", 4) != 0 ||
+ // RIFF = little endian RIFF, RIFX = big endian RIFF
+ if (((qstrncmp(riff.descriptor.id, "RIFF", 4) != 0) && (qstrncmp(riff.descriptor.id, "RIFX", 4) != 0)) ||
qstrncmp(riff.type, "WAVE", 4) != 0) {
source->disconnect(SIGNAL(readyRead()), this, SLOT(handleData()));
emit invalidFormat();
@@ -122,13 +132,17 @@ void QWaveDecoder::handleData()
return;
} else {
state = QWaveDecoder::WaitingForFormatState;
+ if (qstrncmp(riff.descriptor.id, "RIFX", 4) == 0)
+ bigEndian = true;
+ else
+ bigEndian = false;
}
}
if (state == QWaveDecoder::WaitingForFormatState) {
- if (valid = findChunk("fmt ")) {
+ if (findChunk("fmt ")) {
chunk descriptor;
- source->peek(reinterpret_cast<char *>(&descriptor), sizeof(chunk));
+ peekChunk(&descriptor);
if (source->bytesAvailable() < qint64(descriptor.size + sizeof(chunk)))
return;
@@ -138,20 +152,38 @@ void QWaveDecoder::handleData()
if (descriptor.size > sizeof(WAVEHeader))
discardBytes(descriptor.size - sizeof(WAVEHeader));
+ // Swizzle this
+ if (bigEndian) {
+ wave.audioFormat = qFromBigEndian<quint16>(wave.audioFormat);
+ }
+
if (wave.audioFormat != 0 && wave.audioFormat != 1) {
+ // 32bit wave files have format == 0xFFFE (WAVE_FORMAT_EXTENSIBLE).
+ // but don't support them at the moment.
source->disconnect(SIGNAL(readyRead()), this, SLOT(handleData()));
emit invalidFormat();
return;
} else {
- int bps = qFromLittleEndian<quint16>(wave.bitsPerSample);
-
format.setCodec(QLatin1String("audio/pcm"));
- format.setSampleType(bps == 8 ? QAudioFormat::UnSignedInt : QAudioFormat::SignedInt);
- format.setByteOrder(QAudioFormat::LittleEndian);
- format.setFrequency(qFromLittleEndian<quint32>(wave.sampleRate));
- format.setSampleSize(bps);
- format.setChannels(qFromLittleEndian<quint16>(wave.numChannels));
+
+ if (bigEndian) {
+ int bps = qFromBigEndian<quint16>(wave.bitsPerSample);
+
+ format.setSampleType(bps == 8 ? QAudioFormat::UnSignedInt : QAudioFormat::SignedInt);
+ format.setByteOrder(QAudioFormat::BigEndian);
+ format.setFrequency(qFromBigEndian<quint32>(wave.sampleRate));
+ format.setSampleSize(bps);
+ format.setChannels(qFromBigEndian<quint16>(wave.numChannels));
+ } else {
+ int bps = qFromLittleEndian<quint16>(wave.bitsPerSample);
+
+ format.setSampleType(bps == 8 ? QAudioFormat::UnSignedInt : QAudioFormat::SignedInt);
+ format.setByteOrder(QAudioFormat::LittleEndian);
+ format.setFrequency(qFromLittleEndian<quint32>(wave.sampleRate));
+ format.setSampleSize(bps);
+ format.setChannels(qFromLittleEndian<quint16>(wave.numChannels));
+ }
state = QWaveDecoder::WaitingForDataState;
}
@@ -159,11 +191,14 @@ void QWaveDecoder::handleData()
}
if (state == QWaveDecoder::WaitingForDataState) {
- if (valid = findChunk("data")) {
+ if (findChunk("data")) {
source->disconnect(SIGNAL(readyRead()), this, SLOT(handleData()));
chunk descriptor;
source->read(reinterpret_cast<char *>(&descriptor), sizeof(chunk));
+ if (bigEndian)
+ descriptor.size = qFromBigEndian<quint32>(descriptor.size);
+
dataSize = descriptor.size;
haveFormat = true;
@@ -174,22 +209,24 @@ void QWaveDecoder::handleData()
}
}
- if (source->atEnd() || !valid) {
+ if (source->atEnd()) {
source->disconnect(SIGNAL(readyRead()), this, SLOT(handleData()));
emit invalidFormat();
return;
}
-
}
bool QWaveDecoder::enoughDataAvailable()
{
- if (source->bytesAvailable() < qint64(sizeof(chunk)))
+ chunk descriptor;
+ if (!peekChunk(&descriptor))
return false;
- chunk descriptor;
- source->peek(reinterpret_cast<char *>(&descriptor), sizeof(chunk));
+ // This is only called for the RIFF/RIFX header, before bigEndian is set,
+ // so we have to manually swizzle
+ if (qstrncmp(descriptor.id, "RIFX", 4) == 0)
+ descriptor.size = qFromBigEndian<quint32>(descriptor.size);
if (source->bytesAvailable() < qint64(sizeof(chunk) + descriptor.size))
return false;
@@ -199,19 +236,28 @@ bool QWaveDecoder::enoughDataAvailable()
bool QWaveDecoder::findChunk(const char *chunkId)
{
- if (source->bytesAvailable() < qint64(sizeof(chunk)))
- return false;
-
chunk descriptor;
- source->peek(reinterpret_cast<char *>(&descriptor), sizeof(chunk));
+ if (!peekChunk(&descriptor))
+ return false;
if (qstrncmp(descriptor.id, chunkId, 4) == 0)
return true;
- while (source->bytesAvailable() >= qint64(sizeof(chunk) + descriptor.size)) {
- discardBytes(sizeof(chunk) + descriptor.size);
+ // It's possible that bytes->available() is less than the chunk size
+ // if it's corrupt.
+ junkToSkip = qint64(sizeof(chunk) + descriptor.size);
+ while (source->bytesAvailable() > 0) {
+ // Skip the current amount
+ if (junkToSkip > 0)
+ discardBytes(junkToSkip);
- source->peek(reinterpret_cast<char *>(&descriptor), sizeof(chunk));
+ // If we still have stuff left, just exit and try again later
+ // since we can't call peekChunk
+ if (junkToSkip > 0)
+ return false;
+
+ if (!peekChunk(&descriptor))
+ return false;
if (qstrncmp(descriptor.id, chunkId, 4) == 0)
return true;
@@ -220,12 +266,35 @@ bool QWaveDecoder::findChunk(const char *chunkId)
return false;
}
+// Handles endianness
+bool QWaveDecoder::peekChunk(chunk *pChunk)
+{
+ if (source->bytesAvailable() < qint64(sizeof(chunk)))
+ return false;
+
+ source->peek(reinterpret_cast<char *>(pChunk), sizeof(chunk));
+ if (bigEndian)
+ pChunk->size = qFromBigEndian<quint32>(pChunk->size);
+
+ return true;
+}
+
void QWaveDecoder::discardBytes(qint64 numBytes)
{
- if (source->isSequential())
- source->read(numBytes);
- else
+ // Discards a number of bytes
+ // If the iodevice doesn't have this many bytes in it,
+ // remember how much more junk we have to skip.
+ if (source->isSequential()) {
+ QByteArray r = source->read(numBytes); // uggh, wasted memory
+ if (r.size() < numBytes)
+ junkToSkip = numBytes - r.size();
+ else
+ junkToSkip = 0;
+ } else {
+ quint64 origPos = source->pos();
source->seek(source->pos() + numBytes);
+ junkToSkip = origPos + numBytes - source->pos();
+ }
}
QT_END_NAMESPACE
diff --git a/src/multimedia/effects/qwavedecoder_p.h b/src/multimedia/effects/qwavedecoder_p.h
index 6c4c00c25..0d26fec4b 100644
--- a/src/multimedia/effects/qwavedecoder_p.h
+++ b/src/multimedia/effects/qwavedecoder_p.h
@@ -106,6 +106,8 @@ private:
char id[4];
quint32 size;
};
+ bool peekChunk(chunk* pChunk);
+
struct RIFFHeader
{
chunk descriptor;
@@ -124,10 +126,11 @@ private:
bool haveFormat;
qint64 dataSize;
- qint64 remaining;
QAudioFormat format;
QIODevice *source;
State state;
+ quint32 junkToSkip;
+ bool bigEndian;
};
QT_END_NAMESPACE