void Sound::interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray) { CombinedHeader fileHeader; // Create a data stream to analyze the data QDataStream waveStream(const_cast<QByteArray *>(&inputAudioByteArray), QIODevice::ReadOnly); if (waveStream.readRawData(reinterpret_cast<char *>(&fileHeader), sizeof(CombinedHeader)) == sizeof(CombinedHeader)) { if (strncmp(fileHeader.riff.descriptor.id, "RIFF", 4) == 0) { waveStream.setByteOrder(QDataStream::LittleEndian); } else { // descriptor.id == "RIFX" also signifies BigEndian file // waveStream.setByteOrder(QDataStream::BigEndian); qCDebug(audio) << "Currently not supporting big-endian audio files."; return; } if (strncmp(fileHeader.riff.type, "WAVE", 4) != 0 || strncmp(fileHeader.wave.descriptor.id, "fmt", 3) != 0) { qCDebug(audio) << "Not a WAVE Audio file."; return; } // added the endianess check as an extra level of security if (qFromLittleEndian<quint16>(fileHeader.wave.audioFormat) != 1) { qCDebug(audio) << "Currently not supporting non PCM audio files."; return; } if (qFromLittleEndian<quint16>(fileHeader.wave.numChannels) == 2) { _isStereo = true; } else if (qFromLittleEndian<quint16>(fileHeader.wave.numChannels) > 2) { qCDebug(audio) << "Currently not support audio files with more than 2 channels."; } if (qFromLittleEndian<quint16>(fileHeader.wave.bitsPerSample) != 16) { qCDebug(audio) << "Currently not supporting non 16bit audio files."; return; } if (qFromLittleEndian<quint32>(fileHeader.wave.sampleRate) != 48000) { qCDebug(audio) << "Currently not supporting non 48KHz audio files."; return; } // Skip any extra data in the WAVE chunk waveStream.skipRawData(fileHeader.wave.descriptor.size - (sizeof(WAVEHeader) - sizeof(chunk))); // Read off remaining header information DATAHeader dataHeader; while (true) { // Read chunks until the "data" chunk is found if (waveStream.readRawData(reinterpret_cast<char *>(&dataHeader), sizeof(DATAHeader)) == sizeof(DATAHeader)) { if (strncmp(dataHeader.descriptor.id, "data", 4) == 0) { break; } waveStream.skipRawData(dataHeader.descriptor.size); } else { qCDebug(audio) << "Could not read wav audio data header."; return; } } // Now pull out the data quint32 outputAudioByteArraySize = qFromLittleEndian<quint32>(dataHeader.descriptor.size); outputAudioByteArray.resize(outputAudioByteArraySize); if (waveStream.readRawData(outputAudioByteArray.data(), outputAudioByteArraySize) != (int)outputAudioByteArraySize) { qCDebug(audio) << "Error reading WAV file"; } } else { qCDebug(audio) << "Could not read wav audio file header."; return; } }
void Sound::interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray) { CombinedHeader fileHeader; // Create a data stream to analyze the data QDataStream waveStream(const_cast<QByteArray *>(&inputAudioByteArray), QIODevice::ReadOnly); if (waveStream.readRawData(reinterpret_cast<char *>(&fileHeader), sizeof(CombinedHeader)) == sizeof(CombinedHeader)) { if (strncmp(fileHeader.riff.descriptor.id, "RIFF", 4) == 0) { waveStream.setByteOrder(QDataStream::LittleEndian); } else { // descriptor.id == "RIFX" also signifies BigEndian file // waveStream.setByteOrder(QDataStream::BigEndian); qDebug() << "Currently not supporting big-endian audio files."; return; } if (strncmp(fileHeader.riff.type, "WAVE", 4) != 0 || strncmp(fileHeader.wave.descriptor.id, "fmt", 3) != 0) { qDebug() << "Not a WAVE Audio file."; return; } // added the endianess check as an extra level of security if (qFromLittleEndian<quint16>(fileHeader.wave.audioFormat) != 1) { qDebug() << "Currently not supporting non PCM audio files."; return; } if (qFromLittleEndian<quint16>(fileHeader.wave.numChannels) != 1) { qDebug() << "Currently not supporting stereo audio files."; return; } if (qFromLittleEndian<quint16>(fileHeader.wave.bitsPerSample) != 16) { qDebug() << "Currently not supporting non 16bit audio files."; return; } if (qFromLittleEndian<quint32>(fileHeader.wave.sampleRate) != 48000) { qDebug() << "Currently not supporting non 48KHz audio files."; return; } // Read off remaining header information DATAHeader dataHeader; if (waveStream.readRawData(reinterpret_cast<char *>(&dataHeader), sizeof(DATAHeader)) == sizeof(DATAHeader)) { if (strncmp(dataHeader.descriptor.id, "data", 4) != 0) { qDebug() << "Invalid wav audio data header."; return; } } else { qDebug() << "Could not read wav audio data header."; return; } if (qFromLittleEndian<quint32>(fileHeader.riff.descriptor.size) != qFromLittleEndian<quint32>(dataHeader.descriptor.size) + 36) { qDebug() << "Did not read audio file chank headers correctly."; return; } // Now pull out the data quint32 outputAudioByteArraySize = qFromLittleEndian<quint32>(dataHeader.descriptor.size); outputAudioByteArray.resize(outputAudioByteArraySize); waveStream.readRawData(outputAudioByteArray.data(), outputAudioByteArraySize); } else { qDebug() << "Could not read wav audio file header."; return; } }
// returns wavfile sample rate, used for resampling int SoundProcessor::interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray) { // Create a data stream to analyze the data QDataStream waveStream(const_cast<QByteArray *>(&inputAudioByteArray), QIODevice::ReadOnly); // Read the "RIFF" chunk RIFFHeader riff; if (waveStream.readRawData((char*)&riff, sizeof(RIFFHeader)) != sizeof(RIFFHeader)) { qCDebug(audio) << "Not a valid WAVE file."; return 0; } // Parse the "RIFF" chunk if (strncmp(riff.descriptor.id, "RIFF", 4) == 0) { waveStream.setByteOrder(QDataStream::LittleEndian); } else { qCDebug(audio) << "Currently not supporting big-endian audio files."; return 0; } if (strncmp(riff.type, "WAVE", 4) != 0) { qCDebug(audio) << "Not a valid WAVE file."; return 0; } // Read chunks until the "fmt " chunk is found chunk fmt; while (true) { if (waveStream.readRawData((char*)&fmt, sizeof(chunk)) != sizeof(chunk)) { qCDebug(audio) << "Not a valid WAVE file."; return 0; } if (strncmp(fmt.id, "fmt ", 4) == 0) { break; } waveStream.skipRawData(qFromLittleEndian<quint32>(fmt.size)); // next chunk } // Read the "fmt " chunk WAVEFormat wave; if (waveStream.readRawData((char*)&wave, sizeof(WAVEFormat)) != sizeof(WAVEFormat)) { qCDebug(audio) << "Not a valid WAVE file."; return 0; } // Parse the "fmt " chunk if (qFromLittleEndian<quint16>(wave.audioFormat) != WAVEFORMAT_PCM && qFromLittleEndian<quint16>(wave.audioFormat) != WAVEFORMAT_EXTENSIBLE) { qCDebug(audio) << "Currently not supporting non PCM audio files."; return 0; } if (qFromLittleEndian<quint16>(wave.numChannels) == 2) { _isStereo = true; } else if (qFromLittleEndian<quint16>(wave.numChannels) == 4) { _isAmbisonic = true; } else if (qFromLittleEndian<quint16>(wave.numChannels) != 1) { qCDebug(audio) << "Currently not supporting audio files with other than 1/2/4 channels."; return 0; } if (qFromLittleEndian<quint16>(wave.bitsPerSample) != 16) { qCDebug(audio) << "Currently not supporting non 16bit audio files."; return 0; } // Skip any extra data in the "fmt " chunk waveStream.skipRawData(qFromLittleEndian<quint32>(fmt.size) - sizeof(WAVEFormat)); // Read chunks until the "data" chunk is found chunk data; while (true) { if (waveStream.readRawData((char*)&data, sizeof(chunk)) != sizeof(chunk)) { qCDebug(audio) << "Not a valid WAVE file."; return 0; } if (strncmp(data.id, "data", 4) == 0) { break; } waveStream.skipRawData(qFromLittleEndian<quint32>(data.size)); // next chunk } // Read the "data" chunk quint32 outputAudioByteArraySize = qFromLittleEndian<quint32>(data.size); outputAudioByteArray.resize(outputAudioByteArraySize); if (waveStream.readRawData(outputAudioByteArray.data(), outputAudioByteArraySize) != (int)outputAudioByteArraySize) { qCDebug(audio) << "Error reading WAV file"; return 0; } _duration = (float)(outputAudioByteArraySize / (wave.sampleRate * wave.numChannels * wave.bitsPerSample / 8.0f)); return wave.sampleRate; }