/**************************************************************************** ** ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the Technology Preview License Agreement accompanying ** this package. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** ** ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include "utils.h" #include "wavfile.h" struct chunk { char id[4]; quint32 size; }; struct RIFFHeader { chunk descriptor; // "RIFF" char type[4]; // "WAVE" }; 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; DATAHeader data; }; static const int HeaderLength = sizeof(CombinedHeader); WavFile::WavFile(const QAudioFormat &format, qint64 dataLength) : m_format(format) , m_dataLength(dataLength) { } bool WavFile::readHeader(QIODevice &device) { bool result = true; if (!device.isSequential()) result = device.seek(0); // else, assume that current position is the start of the header if (result) { CombinedHeader header; result = (device.read(reinterpret_cast(&header), HeaderLength) == HeaderLength); 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 // PCM ) { if (memcmp(&header.riff.descriptor.id, "RIFF", 4) == 0) m_format.setByteOrder(QAudioFormat::LittleEndian); else m_format.setByteOrder(QAudioFormat::BigEndian); m_format.setChannels(qFromLittleEndian(header.wave.numChannels)); m_format.setCodec("audio/pcm"); m_format.setFrequency(qFromLittleEndian(header.wave.sampleRate)); m_format.setSampleSize(qFromLittleEndian(header.wave.bitsPerSample)); switch(header.wave.bitsPerSample) { case 8: m_format.setSampleType(QAudioFormat::UnSignedInt); break; case 16: m_format.setSampleType(QAudioFormat::SignedInt); break; default: result = false; } m_dataLength = device.size() - HeaderLength; } else { result = false; } } } return result; } bool WavFile::writeHeader(QIODevice &device) { CombinedHeader header; memset(&header, 0, HeaderLength); // RIFF header if (m_format.byteOrder() == QAudioFormat::LittleEndian) strncpy(&header.riff.descriptor.id[0], "RIFF", 4); else strncpy(&header.riff.descriptor.id[0], "RIFX", 4); qToLittleEndian(quint32(m_dataLength + HeaderLength - 8), reinterpret_cast(&header.riff.descriptor.size)); strncpy(&header.riff.type[0], "WAVE", 4); // WAVE header strncpy(&header.wave.descriptor.id[0], "fmt ", 4); qToLittleEndian(quint32(16), reinterpret_cast(&header.wave.descriptor.size)); qToLittleEndian(quint16(1), reinterpret_cast(&header.wave.audioFormat)); qToLittleEndian(quint16(m_format.channels()), reinterpret_cast(&header.wave.numChannels)); qToLittleEndian(quint32(m_format.frequency()), reinterpret_cast(&header.wave.sampleRate)); qToLittleEndian(quint32(m_format.frequency() * m_format.channels() * m_format.sampleSize() / 8), reinterpret_cast(&header.wave.byteRate)); qToLittleEndian(quint16(m_format.channels() * m_format.sampleSize() / 8), reinterpret_cast(&header.wave.blockAlign)); qToLittleEndian(quint16(m_format.sampleSize()), reinterpret_cast(&header.wave.bitsPerSample)); // DATA header strncpy(&header.data.descriptor.id[0], "data", 4); qToLittleEndian(quint32(m_dataLength), reinterpret_cast(&header.data.descriptor.size)); return (device.write(reinterpret_cast(&header), HeaderLength) == HeaderLength); } const QAudioFormat& WavFile::format() const { return m_format; } qint64 WavFile::dataLength() const { return m_dataLength; } qint64 WavFile::headerLength() { return HeaderLength; } bool WavFile::writeDataLength(QIODevice &device, qint64 dataLength) { bool result = false; if (!device.isSequential()) { device.seek(40); unsigned char dataLengthLE[4]; qToLittleEndian(quint32(dataLength), dataLengthLE); result = (device.write(reinterpret_cast(dataLengthLE), 4) == 4); } return result; } #include #include qint64 WavFile::readData(QIODevice &device, QByteArray &buffer, QAudioFormat outputFormat) { if (QAudioFormat() == outputFormat) outputFormat = m_format; qint64 result = 0; QFile file("wav.txt"); file.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream stream; stream.setDevice(&file); if (isPCMS16LE(outputFormat) && isPCMS16LE(m_format)) { QVector inputSample(2 * m_format.channels()); qint16 *output = reinterpret_cast(buffer.data()); while (result < buffer.size()) { if (device.read(inputSample.data(), inputSample.count())) { int inputIdx = 0; for (int outputIdx = 0; outputIdx < outputFormat.channels(); ++outputIdx) { const qint16* input = reinterpret_cast(inputSample.data() + 2 * inputIdx); *output++ = qFromLittleEndian(*input); result += 2; if (inputIdx < m_format.channels()) ++inputIdx; } } else { break; } } } return result; }