bool QWaveDecoder::findChunk(const char *chunkId) { chunk descriptor; do { if (!peekChunk(&descriptor)) return false; if (qstrncmp(descriptor.id, chunkId, 4) == 0) return true; // It's possible that bytes->available() is less than the chunk size // if it's corrupt. junkToSkip = qint64(sizeof(chunk) + descriptor.size); // Skip the current amount if (junkToSkip > 0) discardBytes(junkToSkip); // If we still have stuff left, just exit and try again later // since we can't call peekChunk if (junkToSkip > 0) return false; } while (source->bytesAvailable() > 0); return false; }
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 (qstrncmp(descriptor.id, chunkId, 4) == 0) return true; while (source->bytesAvailable() >= qint64(sizeof(chunk) + descriptor.size)) { discardBytes(sizeof(chunk) + descriptor.size); source->peek(reinterpret_cast<char *>(&descriptor), sizeof(chunk)); if (qstrncmp(descriptor.id, chunkId, 4) == 0) return true; } return false; }
void QWaveDecoder::handleData() { // 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) { // We might have run out if (source->atEnd()) parsingFailed(); return; } } if (state == QWaveDecoder::InitialState) { if (source->bytesAvailable() < qint64(sizeof(RIFFHeader))) return; RIFFHeader riff; source->read(reinterpret_cast<char *>(&riff), sizeof(RIFFHeader)); // 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) { parsingFailed(); return; } else { state = QWaveDecoder::WaitingForFormatState; if (qstrncmp(riff.descriptor.id, "RIFX", 4) == 0) bigEndian = true; else bigEndian = false; } } if (state == QWaveDecoder::WaitingForFormatState) { if (findChunk("fmt ")) { chunk descriptor; peekChunk(&descriptor); quint32 rawChunkSize = descriptor.size + sizeof(chunk); if (source->bytesAvailable() < qint64(rawChunkSize)) return; WAVEHeader wave; source->read(reinterpret_cast<char *>(&wave), sizeof(WAVEHeader)); if (rawChunkSize > sizeof(WAVEHeader)) discardBytes(rawChunkSize - sizeof(WAVEHeader)); // Swizzle this if (bigEndian) { wave.audioFormat = qFromBigEndian<quint16>(wave.audioFormat); } else { wave.audioFormat = qFromLittleEndian<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. parsingFailed(); return; } else { format.setCodec(QLatin1String("audio/pcm")); if (bigEndian) { int bps = qFromBigEndian<quint16>(wave.bitsPerSample); format.setSampleType(bps == 8 ? QAudioFormat::UnSignedInt : QAudioFormat::SignedInt); format.setByteOrder(QAudioFormat::BigEndian); format.setSampleRate(qFromBigEndian<quint32>(wave.sampleRate)); format.setSampleSize(bps); format.setChannelCount(qFromBigEndian<quint16>(wave.numChannels)); } else { int bps = qFromLittleEndian<quint16>(wave.bitsPerSample); format.setSampleType(bps == 8 ? QAudioFormat::UnSignedInt : QAudioFormat::SignedInt); format.setByteOrder(QAudioFormat::LittleEndian); format.setSampleRate(qFromLittleEndian<quint32>(wave.sampleRate)); format.setSampleSize(bps); format.setChannelCount(qFromLittleEndian<quint16>(wave.numChannels)); } state = QWaveDecoder::WaitingForDataState; } } } if (state == QWaveDecoder::WaitingForDataState) { 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); else descriptor.size = qFromLittleEndian<quint32>(descriptor.size); dataSize = descriptor.size; haveFormat = true; connect(source, SIGNAL(readyRead()), SIGNAL(readyRead())); emit formatKnown(); return; } } // If we hit the end without finding data, it's a parsing error if (source->atEnd()) { parsingFailed(); } }
void QWaveDecoder::handleData() { if (state == QWaveDecoder::InitialState) { if (source->bytesAvailable() < qint64(sizeof(RIFFHeader))) return; RIFFHeader riff; source->read(reinterpret_cast<char *>(&riff), sizeof(RIFFHeader)); if (qstrncmp(riff.descriptor.id, "RIFF", 4) != 0 || qstrncmp(riff.type, "WAVE", 4) != 0) { source->disconnect(SIGNAL(readyRead()), this, SLOT(handleData())); emit invalidFormat(); return; } else { state = QWaveDecoder::WaitingForFormatState; } } if (state == QWaveDecoder::WaitingForFormatState) { if (findChunk("fmt ")) { chunk descriptor; source->peek(reinterpret_cast<char *>(&descriptor), sizeof(chunk)); if (source->bytesAvailable() < qint64(descriptor.size + sizeof(chunk))) return; WAVEHeader wave; source->read(reinterpret_cast<char *>(&wave), sizeof(WAVEHeader)); if (descriptor.size > sizeof(WAVEHeader)) discardBytes(descriptor.size - sizeof(WAVEHeader)); if (wave.audioFormat != 0 && wave.audioFormat != 1) { 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)); state = QWaveDecoder::WaitingForDataState; } } } if (state == QWaveDecoder::WaitingForDataState) { if (findChunk("data")) { source->disconnect(SIGNAL(readyRead()), this, SLOT(handleData())); chunk descriptor; source->read(reinterpret_cast<char *>(&descriptor), sizeof(chunk)); dataSize = descriptor.size; haveFormat = true; connect(source, SIGNAL(readyRead()), SIGNAL(readyRead())); emit formatKnown(); return; } } if (source->atEnd()) { source->disconnect(SIGNAL(readyRead()), this, SLOT(handleData())); emit invalidFormat(); return; } }