source: trunk/demos/spectrum/app/wavfile.cpp@ 769

Last change on this file since 769 was 769, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.3 sources from branches/vendor/nokia/qt.

File size: 8.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the examples of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:BSD$
10** You may use this file under the terms of the BSD license as follows:
11**
12** "Redistribution and use in source and binary forms, with or without
13** modification, are permitted provided that the following conditions are
14** met:
15** * Redistributions of source code must retain the above copyright
16** notice, this list of conditions and the following disclaimer.
17** * Redistributions in binary form must reproduce the above copyright
18** notice, this list of conditions and the following disclaimer in
19** the documentation and/or other materials provided with the
20** distribution.
21** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
22** the names of its contributors may be used to endorse or promote
23** products derived from this software without specific prior written
24** permission.
25**
26** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#include <QtCore/qendian.h>
42#include <QVector>
43#include <QDebug>
44#include "utils.h"
45#include "wavfile.h"
46
47struct chunk
48{
49 char id[4];
50 quint32 size;
51};
52
53struct RIFFHeader
54{
55 chunk descriptor; // "RIFF"
56 char type[4]; // "WAVE"
57};
58
59struct WAVEHeader
60{
61 chunk descriptor;
62 quint16 audioFormat;
63 quint16 numChannels;
64 quint32 sampleRate;
65 quint32 byteRate;
66 quint16 blockAlign;
67 quint16 bitsPerSample;
68};
69
70struct DATAHeader
71{
72 chunk descriptor;
73};
74
75struct CombinedHeader
76{
77 RIFFHeader riff;
78 WAVEHeader wave;
79 DATAHeader data;
80};
81
82static const int HeaderLength = sizeof(CombinedHeader);
83
84
85WavFile::WavFile(const QAudioFormat &format, qint64 dataLength)
86 : m_format(format)
87 , m_dataLength(dataLength)
88{
89
90}
91
92bool WavFile::readHeader(QIODevice &device)
93{
94 bool result = true;
95
96 if (!device.isSequential())
97 result = device.seek(0);
98 // else, assume that current position is the start of the header
99
100 if (result) {
101 CombinedHeader header;
102 result = (device.read(reinterpret_cast<char *>(&header), HeaderLength) == HeaderLength);
103 if (result) {
104 if ((memcmp(&header.riff.descriptor.id, "RIFF", 4) == 0
105 || memcmp(&header.riff.descriptor.id, "RIFX", 4) == 0)
106 && memcmp(&header.riff.type, "WAVE", 4) == 0
107 && memcmp(&header.wave.descriptor.id, "fmt ", 4) == 0
108 && header.wave.audioFormat == 1 // PCM
109 ) {
110 if (memcmp(&header.riff.descriptor.id, "RIFF", 4) == 0)
111 m_format.setByteOrder(QAudioFormat::LittleEndian);
112 else
113 m_format.setByteOrder(QAudioFormat::BigEndian);
114
115 m_format.setChannels(qFromLittleEndian<quint16>(header.wave.numChannels));
116 m_format.setCodec("audio/pcm");
117 m_format.setFrequency(qFromLittleEndian<quint32>(header.wave.sampleRate));
118 m_format.setSampleSize(qFromLittleEndian<quint16>(header.wave.bitsPerSample));
119
120 switch(header.wave.bitsPerSample) {
121 case 8:
122 m_format.setSampleType(QAudioFormat::UnSignedInt);
123 break;
124 case 16:
125 m_format.setSampleType(QAudioFormat::SignedInt);
126 break;
127 default:
128 result = false;
129 }
130
131 m_dataLength = device.size() - HeaderLength;
132 } else {
133 result = false;
134 }
135 }
136 }
137
138 return result;
139}
140
141bool WavFile::writeHeader(QIODevice &device)
142{
143 CombinedHeader header;
144
145 memset(&header, 0, HeaderLength);
146
147 // RIFF header
148 if (m_format.byteOrder() == QAudioFormat::LittleEndian)
149 strncpy(&header.riff.descriptor.id[0], "RIFF", 4);
150 else
151 strncpy(&header.riff.descriptor.id[0], "RIFX", 4);
152 qToLittleEndian<quint32>(quint32(m_dataLength + HeaderLength - 8),
153 reinterpret_cast<unsigned char*>(&header.riff.descriptor.size));
154 strncpy(&header.riff.type[0], "WAVE", 4);
155
156 // WAVE header
157 strncpy(&header.wave.descriptor.id[0], "fmt ", 4);
158 qToLittleEndian<quint32>(quint32(16),
159 reinterpret_cast<unsigned char*>(&header.wave.descriptor.size));
160 qToLittleEndian<quint16>(quint16(1),
161 reinterpret_cast<unsigned char*>(&header.wave.audioFormat));
162 qToLittleEndian<quint16>(quint16(m_format.channels()),
163 reinterpret_cast<unsigned char*>(&header.wave.numChannels));
164 qToLittleEndian<quint32>(quint32(m_format.frequency()),
165 reinterpret_cast<unsigned char*>(&header.wave.sampleRate));
166 qToLittleEndian<quint32>(quint32(m_format.frequency() * m_format.channels() * m_format.sampleSize() / 8),
167 reinterpret_cast<unsigned char*>(&header.wave.byteRate));
168 qToLittleEndian<quint16>(quint16(m_format.channels() * m_format.sampleSize() / 8),
169 reinterpret_cast<unsigned char*>(&header.wave.blockAlign));
170 qToLittleEndian<quint16>(quint16(m_format.sampleSize()),
171 reinterpret_cast<unsigned char*>(&header.wave.bitsPerSample));
172
173 // DATA header
174 strncpy(&header.data.descriptor.id[0], "data", 4);
175 qToLittleEndian<quint32>(quint32(m_dataLength),
176 reinterpret_cast<unsigned char*>(&header.data.descriptor.size));
177
178 return (device.write(reinterpret_cast<const char *>(&header), HeaderLength) == HeaderLength);
179}
180
181const QAudioFormat& WavFile::format() const
182{
183 return m_format;
184}
185
186qint64 WavFile::dataLength() const
187{
188 return m_dataLength;
189}
190
191qint64 WavFile::headerLength()
192{
193 return HeaderLength;
194}
195
196bool WavFile::writeDataLength(QIODevice &device, qint64 dataLength)
197{
198 bool result = false;
199 if (!device.isSequential()) {
200 device.seek(40);
201 unsigned char dataLengthLE[4];
202 qToLittleEndian<quint32>(quint32(dataLength), dataLengthLE);
203 result = (device.write(reinterpret_cast<const char *>(dataLengthLE), 4) == 4);
204 }
205 return result;
206}
207
208#include <QFile>
209#include <QTextStream>
210
211qint64 WavFile::readData(QIODevice &device, QByteArray &buffer,
212 QAudioFormat outputFormat)
213{
214 if (QAudioFormat() == outputFormat)
215 outputFormat = m_format;
216
217 qint64 result = 0;
218
219 QFile file("wav.txt");
220 file.open(QIODevice::WriteOnly | QIODevice::Text);
221 QTextStream stream;
222 stream.setDevice(&file);
223
224 if (isPCMS16LE(outputFormat) && isPCMS16LE(m_format)) {
225 QVector<char> inputSample(2 * m_format.channels());
226
227 qint16 *output = reinterpret_cast<qint16*>(buffer.data());
228
229 while (result < buffer.size()) {
230 if (device.read(inputSample.data(), inputSample.count())) {
231 int inputIdx = 0;
232 for (int outputIdx = 0; outputIdx < outputFormat.channels(); ++outputIdx) {
233 const qint16* input = reinterpret_cast<const qint16*>(inputSample.data() + 2 * inputIdx);
234 *output++ = qFromLittleEndian<qint16>(*input);
235 result += 2;
236 if (inputIdx < m_format.channels())
237 ++inputIdx;
238 }
239 } else {
240 break;
241 }
242 }
243 }
244 return result;
245}
246
Note: See TracBrowser for help on using the repository browser.