|
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:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include <QtCore/qendian.h> |
|
43 #include <QVector> |
|
44 #include <QDebug> |
|
45 #include "utils.h" |
|
46 #include "wavfile.h" |
|
47 |
|
48 struct chunk |
|
49 { |
|
50 char id[4]; |
|
51 quint32 size; |
|
52 }; |
|
53 |
|
54 struct RIFFHeader |
|
55 { |
|
56 chunk descriptor; // "RIFF" |
|
57 char type[4]; // "WAVE" |
|
58 }; |
|
59 |
|
60 struct WAVEHeader |
|
61 { |
|
62 chunk descriptor; |
|
63 quint16 audioFormat; |
|
64 quint16 numChannels; |
|
65 quint32 sampleRate; |
|
66 quint32 byteRate; |
|
67 quint16 blockAlign; |
|
68 quint16 bitsPerSample; |
|
69 }; |
|
70 |
|
71 struct DATAHeader |
|
72 { |
|
73 chunk descriptor; |
|
74 }; |
|
75 |
|
76 struct CombinedHeader |
|
77 { |
|
78 RIFFHeader riff; |
|
79 WAVEHeader wave; |
|
80 DATAHeader data; |
|
81 }; |
|
82 |
|
83 static const int HeaderLength = sizeof(CombinedHeader); |
|
84 |
|
85 |
|
86 WavFile::WavFile(const QAudioFormat &format, qint64 dataLength) |
|
87 : m_format(format) |
|
88 , m_dataLength(dataLength) |
|
89 { |
|
90 |
|
91 } |
|
92 |
|
93 bool WavFile::readHeader(QIODevice &device) |
|
94 { |
|
95 bool result = true; |
|
96 |
|
97 if (!device.isSequential()) |
|
98 result = device.seek(0); |
|
99 // else, assume that current position is the start of the header |
|
100 |
|
101 if (result) { |
|
102 CombinedHeader header; |
|
103 result = (device.read(reinterpret_cast<char *>(&header), HeaderLength) == HeaderLength); |
|
104 if (result) { |
|
105 if ((memcmp(&header.riff.descriptor.id, "RIFF", 4) == 0 |
|
106 || memcmp(&header.riff.descriptor.id, "RIFX", 4) == 0) |
|
107 && memcmp(&header.riff.type, "WAVE", 4) == 0 |
|
108 && memcmp(&header.wave.descriptor.id, "fmt ", 4) == 0 |
|
109 && header.wave.audioFormat == 1 // PCM |
|
110 ) { |
|
111 if (memcmp(&header.riff.descriptor.id, "RIFF", 4) == 0) |
|
112 m_format.setByteOrder(QAudioFormat::LittleEndian); |
|
113 else |
|
114 m_format.setByteOrder(QAudioFormat::BigEndian); |
|
115 |
|
116 m_format.setChannels(qFromLittleEndian<quint16>(header.wave.numChannels)); |
|
117 m_format.setCodec("audio/pcm"); |
|
118 m_format.setFrequency(qFromLittleEndian<quint32>(header.wave.sampleRate)); |
|
119 m_format.setSampleSize(qFromLittleEndian<quint16>(header.wave.bitsPerSample)); |
|
120 |
|
121 switch(header.wave.bitsPerSample) { |
|
122 case 8: |
|
123 m_format.setSampleType(QAudioFormat::UnSignedInt); |
|
124 break; |
|
125 case 16: |
|
126 m_format.setSampleType(QAudioFormat::SignedInt); |
|
127 break; |
|
128 default: |
|
129 result = false; |
|
130 } |
|
131 |
|
132 m_dataLength = device.size() - HeaderLength; |
|
133 } else { |
|
134 result = false; |
|
135 } |
|
136 } |
|
137 } |
|
138 |
|
139 return result; |
|
140 } |
|
141 |
|
142 bool WavFile::writeHeader(QIODevice &device) |
|
143 { |
|
144 CombinedHeader header; |
|
145 |
|
146 memset(&header, 0, HeaderLength); |
|
147 |
|
148 // RIFF header |
|
149 if (m_format.byteOrder() == QAudioFormat::LittleEndian) |
|
150 strncpy(&header.riff.descriptor.id[0], "RIFF", 4); |
|
151 else |
|
152 strncpy(&header.riff.descriptor.id[0], "RIFX", 4); |
|
153 qToLittleEndian<quint32>(quint32(m_dataLength + HeaderLength - 8), |
|
154 reinterpret_cast<unsigned char*>(&header.riff.descriptor.size)); |
|
155 strncpy(&header.riff.type[0], "WAVE", 4); |
|
156 |
|
157 // WAVE header |
|
158 strncpy(&header.wave.descriptor.id[0], "fmt ", 4); |
|
159 qToLittleEndian<quint32>(quint32(16), |
|
160 reinterpret_cast<unsigned char*>(&header.wave.descriptor.size)); |
|
161 qToLittleEndian<quint16>(quint16(1), |
|
162 reinterpret_cast<unsigned char*>(&header.wave.audioFormat)); |
|
163 qToLittleEndian<quint16>(quint16(m_format.channels()), |
|
164 reinterpret_cast<unsigned char*>(&header.wave.numChannels)); |
|
165 qToLittleEndian<quint32>(quint32(m_format.frequency()), |
|
166 reinterpret_cast<unsigned char*>(&header.wave.sampleRate)); |
|
167 qToLittleEndian<quint32>(quint32(m_format.frequency() * m_format.channels() * m_format.sampleSize() / 8), |
|
168 reinterpret_cast<unsigned char*>(&header.wave.byteRate)); |
|
169 qToLittleEndian<quint16>(quint16(m_format.channels() * m_format.sampleSize() / 8), |
|
170 reinterpret_cast<unsigned char*>(&header.wave.blockAlign)); |
|
171 qToLittleEndian<quint16>(quint16(m_format.sampleSize()), |
|
172 reinterpret_cast<unsigned char*>(&header.wave.bitsPerSample)); |
|
173 |
|
174 // DATA header |
|
175 strncpy(&header.data.descriptor.id[0], "data", 4); |
|
176 qToLittleEndian<quint32>(quint32(m_dataLength), |
|
177 reinterpret_cast<unsigned char*>(&header.data.descriptor.size)); |
|
178 |
|
179 return (device.write(reinterpret_cast<const char *>(&header), HeaderLength) == HeaderLength); |
|
180 } |
|
181 |
|
182 const QAudioFormat& WavFile::format() const |
|
183 { |
|
184 return m_format; |
|
185 } |
|
186 |
|
187 qint64 WavFile::dataLength() const |
|
188 { |
|
189 return m_dataLength; |
|
190 } |
|
191 |
|
192 qint64 WavFile::headerLength() |
|
193 { |
|
194 return HeaderLength; |
|
195 } |
|
196 |
|
197 bool WavFile::writeDataLength(QIODevice &device, qint64 dataLength) |
|
198 { |
|
199 bool result = false; |
|
200 if (!device.isSequential()) { |
|
201 device.seek(40); |
|
202 unsigned char dataLengthLE[4]; |
|
203 qToLittleEndian<quint32>(quint32(dataLength), dataLengthLE); |
|
204 result = (device.write(reinterpret_cast<const char *>(dataLengthLE), 4) == 4); |
|
205 } |
|
206 return result; |
|
207 } |
|
208 |
|
209 #include <QFile> |
|
210 #include <QTextStream> |
|
211 |
|
212 qint64 WavFile::readData(QIODevice &device, QByteArray &buffer, |
|
213 QAudioFormat outputFormat) |
|
214 { |
|
215 if (QAudioFormat() == outputFormat) |
|
216 outputFormat = m_format; |
|
217 |
|
218 qint64 result = 0; |
|
219 |
|
220 QFile file("wav.txt"); |
|
221 file.open(QIODevice::WriteOnly | QIODevice::Text); |
|
222 QTextStream stream; |
|
223 stream.setDevice(&file); |
|
224 |
|
225 if (isPCMS16LE(outputFormat) && isPCMS16LE(m_format)) { |
|
226 QVector<char> inputSample(2 * m_format.channels()); |
|
227 |
|
228 qint16 *output = reinterpret_cast<qint16*>(buffer.data()); |
|
229 |
|
230 while (result < buffer.size()) { |
|
231 if (device.read(inputSample.data(), inputSample.count())) { |
|
232 int inputIdx = 0; |
|
233 for (int outputIdx = 0; outputIdx < outputFormat.channels(); ++outputIdx) { |
|
234 const qint16* input = reinterpret_cast<const qint16*>(inputSample.data() + 2 * inputIdx); |
|
235 *output++ = qFromLittleEndian<qint16>(*input); |
|
236 result += 2; |
|
237 if (inputIdx < m_format.channels()) |
|
238 ++inputIdx; |
|
239 } |
|
240 } else { |
|
241 break; |
|
242 } |
|
243 } |
|
244 } |
|
245 return result; |
|
246 } |
|
247 |