diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/multimedia/effects/qwavedecoder_p.cpp | 127 | ||||
-rw-r--r-- | src/multimedia/effects/qwavedecoder_p.h | 5 |
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 |