Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwavedecoder.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:data-parser
4
5#include "qwavedecoder.h"
6
7#include <QtCore/qdebug.h>
8#include <QtCore/qendian.h>
9#include <QtCore/qsysinfo.h>
10#include <QtCore/qtimer.h>
11#include <QtCore/qbytearray.h>
12
13#include <limits.h>
14
15#include <dr_wav.h>
16
17QT_BEGIN_NAMESPACE
18
19#if QT_DEPRECATED_SINCE(6, 11)
20
21namespace QtMultimediaPrivate {
22
23class QWaveDecoderOldLayout : public QIODevice
24{
25 enum State { InitialState, WaitingForFormatState, WaitingForDataState };
26
27public:
28 bool haveFormat = false;
29 bool haveHeader = false;
30 qint64 dataSize = 0;
31 QIODevice *device = nullptr;
32 QAudioFormat format;
33 State state = InitialState;
34 quint32 junkToSkip = 0;
35 bool bigEndian = false;
36 bool byteSwap = false;
37 int bps = 0;
38};
39
40static_assert(sizeof(QWaveDecoder) == sizeof(QtMultimediaPrivate::QWaveDecoderOldLayout),
41 "QWaveDecoder ABI size mismatch — adjust fields or QWaveDecoderNewFields");
42
43} // namespace QtMultimediaPrivate
44
45QWaveDecoder::QWaveDecoder(QIODevice *device, QObject *parent)
46 : QIODevice(parent),
47 device(device)
48{
49}
50
51QWaveDecoder::QWaveDecoder(QIODevice *device, const QAudioFormat &format, QObject *parent)
52 : QIODevice(parent),
53 device(device),
54 format(format)
55{
56}
57
58QWaveDecoder::~QWaveDecoder()
59{
60 m_headerBuf.reset();
61}
62
63bool QWaveDecoder::open(QIODevice::OpenMode mode)
64{
65 bool canOpen = false;
66 if (mode & QIODevice::ReadOnly && mode & ~QIODevice::WriteOnly) {
67 canOpen = QIODevice::open(mode | QIODevice::Unbuffered);
68 if (canOpen) {
69 m_headerBuf = std::make_unique<QByteArray>();
70 connect(device, &QIODevice::readyRead, this, &QWaveDecoder::handleData);
71 // Try immediately if data already available
72 if (device->bytesAvailable() > 0)
73 handleData();
74 }
75 return canOpen;
76 }
77
78 if (mode & QIODevice::WriteOnly) {
79 if (format.sampleFormat() != QAudioFormat::Int16)
80 return false;
81 canOpen = QIODevice::open(mode);
82 if (canOpen && writeHeader())
83 haveHeader = true;
84 return canOpen;
85 }
86 return QIODevice::open(mode);
87}
88
89void QWaveDecoder::close()
90{
91 if (isOpen() && (openMode() & QIODevice::WriteOnly)) {
92 Q_ASSERT(dataSize < INT_MAX);
93 if (!device->isOpen() || !writeDataLength())
94 qWarning() << "Failed to finalize wav file";
95 }
96
97 m_headerBuf.reset();
98
99 QIODevice::close();
100}
101
102bool QWaveDecoder::seek(qint64 pos)
103{
104 return device->seek(pos);
105}
106
107qint64 QWaveDecoder::pos() const
108{
109 return device->pos();
110}
111
112void QWaveDecoder::setIODevice(QIODevice * /* device */)
113{
114}
115
116QAudioFormat QWaveDecoder::audioFormat() const
117{
118 return format;
119}
120
121QIODevice* QWaveDecoder::getDevice()
122{
123 return device;
124}
125
126int QWaveDecoder::duration() const
127{
128 if (openMode() & QIODevice::WriteOnly)
129 return 0;
130 int bytesPerSec = format.bytesPerFrame() * format.sampleRate();
131 return bytesPerSec ? size() * 1000 / bytesPerSec : 0;
132}
133
134qint64 QWaveDecoder::size() const
135{
136 if (openMode() & QIODevice::ReadOnly) {
137 if (!haveFormat)
138 return 0;
139 return dataSize;
140 } else {
141 return device->size();
142 }
143}
144
145bool QWaveDecoder::isSequential() const
146{
147 return device->isSequential();
148}
149
150qint64 QWaveDecoder::bytesAvailable() const
151{
152 return haveFormat ? device->bytesAvailable() : 0;
153}
154
155qint64 QWaveDecoder::headerLength()
156{
157 return HeaderLength;
158}
159
160qint64 QWaveDecoder::readData(char *data, qint64 maxlen)
161{
162 const int bytesPerSample = format.bytesPerSample();
163 if (!haveFormat || bytesPerSample == 0)
164 return 0;
165
166 // Align to sample boundary
167 maxlen = (maxlen / bytesPerSample) * bytesPerSample;
168 if (maxlen == 0)
169 return 0;
170
171 qint64 totalRead = 0;
172 char *dst = data;
173
174 // For sequential devices, drain the PCM prefix buffer first.
175 // This buffer holds bytes already consumed from the device during header parsing.
176 if (m_headerBuf && !m_headerBuf->isEmpty()) {
177 qint64 fromBuf = qMin(maxlen, qint64(m_headerBuf->size()));
178 // Align to sample boundary
179 fromBuf = (fromBuf / bytesPerSample) * bytesPerSample;
180 if (fromBuf > 0) {
181 memcpy(dst, m_headerBuf->constData(), size_t(fromBuf));
182 m_headerBuf->remove(0, static_cast<qsizetype>(fromBuf));
183 totalRead += fromBuf;
184 dst += fromBuf;
185 maxlen -= fromBuf;
186 }
187 if (m_headerBuf->isEmpty())
188 m_headerBuf.reset();
189 }
190
191 // Read remainder from device
192 if (maxlen > 0) {
193 qint64 read = device->read(dst, maxlen);
194 if (read > 0)
195 totalRead += read;
196 }
197
198 // Byte-swap the entire output for big-endian (RIFX) WAV on LE host (or vice versa)
199 if (m_byteSwap && format.bytesPerFrame() > 1 && totalRead > 0) {
200 qint64 nSamples = totalRead / bytesPerSample;
201 switch (bytesPerSample) {
202 case 2: qbswap<2>(data, qsizetype(nSamples), data); break;
203 case 4: qbswap<4>(data, qsizetype(nSamples), data); break;
204 default: Q_UNREACHABLE();
205 }
206 }
207
208 return totalRead;
209}
210
211qint64 QWaveDecoder::writeData(const char *data, qint64 len)
212{
213 if (!haveHeader)
214 return 0;
215 qint64 written = device->write(data, len);
216 dataSize += written;
217 return written;
218}
219
220bool QWaveDecoder::writeHeader()
221{
222 if (device->size() != 0)
223 return false;
224
225#ifndef Q_LITTLE_ENDIAN
226 return false;
227#endif
228
229 CombinedHeader header;
230 memset(&header, 0, HeaderLength);
231
232 memcpy(header.riff.descriptor.id, "RIFF", 4);
233 qToLittleEndian<quint32>(quint32(dataSize + HeaderLength - 8),
234 reinterpret_cast<unsigned char*>(&header.riff.descriptor.size));
235 memcpy(header.riff.type, "WAVE", 4);
236
237 memcpy(header.wave.descriptor.id, "fmt ", 4);
238 qToLittleEndian<quint32>(quint32(16),
239 reinterpret_cast<unsigned char*>(&header.wave.descriptor.size));
240 qToLittleEndian<quint16>(quint16(1),
241 reinterpret_cast<unsigned char*>(&header.wave.audioFormat));
242 qToLittleEndian<quint16>(quint16(format.channelCount()),
243 reinterpret_cast<unsigned char*>(&header.wave.numChannels));
244 qToLittleEndian<quint32>(quint32(format.sampleRate()),
245 reinterpret_cast<unsigned char*>(&header.wave.sampleRate));
246 qToLittleEndian<quint32>(quint32(format.sampleRate() * format.bytesPerFrame()),
247 reinterpret_cast<unsigned char*>(&header.wave.byteRate));
248 qToLittleEndian<quint16>(quint16(format.channelCount() * format.bytesPerSample()),
249 reinterpret_cast<unsigned char*>(&header.wave.blockAlign));
250 qToLittleEndian<quint16>(quint16(format.bytesPerSample() * 8),
251 reinterpret_cast<unsigned char*>(&header.wave.bitsPerSample));
252
253 memcpy(header.data.descriptor.id, "data", 4);
254 qToLittleEndian<quint32>(quint32(dataSize),
255 reinterpret_cast<unsigned char*>(&header.data.descriptor.size));
256
257 return device->write(reinterpret_cast<const char *>(&header), HeaderLength);
258}
259
260bool QWaveDecoder::writeDataLength()
261{
262#ifndef Q_LITTLE_ENDIAN
263 return false;
264#endif
265 if (isSequential())
266 return false;
267
268 if (!device->seek(4)) {
269 qDebug() << "can't seek";
270 return false;
271 }
272
273 quint32 length = quint32(dataSize + HeaderLength - 8);
274 if (device->write(reinterpret_cast<const char *>(&length), 4) != 4)
275 return false;
276
277 if (!device->seek(40))
278 return false;
279
280 return device->write(reinterpret_cast<const char *>(&dataSize), 4);
281}
282
283void QWaveDecoder::parsingFailed()
284{
285 m_headerBuf.reset();
286
287 Q_ASSERT(device);
288 disconnect(device, &QIODevice::readyRead, this, &QWaveDecoder::handleData);
289 emit parsingError();
290}
291
292void QWaveDecoder::handleData()
293{
294 using namespace QtPrivate;
295
296 if (openMode() == QIODevice::WriteOnly)
297 return;
298
299 if (haveFormat) {
300 // Already parsed — relay readyRead from device to our listeners
301 disconnect(device, &QIODevice::readyRead, this, &QWaveDecoder::handleData);
302 connect(device, &QIODevice::readyRead, this, &QIODevice::readyRead);
303 return;
304 }
305
306 if (!m_headerBuf)
307 return;
308
309 // Accumulate all available bytes into the header buffer
310 QByteArray incoming = device->readAll();
311 if (!incoming.isEmpty())
312 m_headerBuf->append(incoming);
313
314 // Need at least the RIFF header + fmt chunk to attempt parsing
315 if (m_headerBuf->size() < int(sizeof(RIFFHeader)) + int(sizeof(chunk)))
316 return;
317
318 // Try to parse the accumulated buffer with dr_wav in-memory mode
319 drwav wav;
320 if (!drwav_init_memory(&wav, m_headerBuf->constData(), size_t(m_headerBuf->size()), nullptr)) {
321 // Not enough data yet — wait for more readyRead signals
322 // (but only if the device isn't done)
323 if (device->atEnd())
324 parsingFailed();
325 return;
326 }
327
328 // dr_wav parsed the header successfully. Extract what we need.
329 drwav_uint16 audioFormat = drwav_fmt_get_format(&wav.fmt);
330
331 if (audioFormat != 0 && audioFormat != 1) {
332 // Not PCM (e.g. float, ADPCM, extensible) — reject
333 drwav_uninit(&wav);
334 parsingFailed();
335 return;
336 }
337
338 int bitsPerSample = wav.bitsPerSample;
339 int sampleRate = int(wav.sampleRate);
340 int channels = int(wav.channels);
341
342 // Only 8-bit and 16-bit PCM supported
343 QAudioFormat::SampleFormat fmt = QAudioFormat::Unknown;
344 switch (bitsPerSample) {
345 case 8: fmt = QAudioFormat::UInt8; break;
346 case 16: fmt = QAudioFormat::Int16; break;
347 default: break; // 24-bit, 32-bit, float — rejected
348 }
349
350 if (fmt == QAudioFormat::Unknown || sampleRate == 0 || channels == 0) {
351 drwav_uninit(&wav);
352 parsingFailed();
353 return;
354 }
355
356 // Endianness: RIFX = big-endian container
357 bool bigEndian = (wav.container == drwav_container_rifx);
358 m_byteSwap = (bigEndian != (QSysInfo::ByteOrder == QSysInfo::BigEndian));
359
360 qint64 dataChunkDataPos = qint64(wav.dataChunkDataPos);
361 dataSize = qint64(wav.dataChunkDataSize);
362
363 drwav_uninit(&wav);
364
365 // Seek the device to the PCM data start
366 if (!device->isSequential()) {
367 if (!device->seek(dataChunkDataPos)) {
368 parsingFailed();
369 return;
370 }
371 // No longer need the header buffer for non-sequential
372 m_headerBuf.reset();
373 } else {
374 // Sequential device: m_headerBuf already holds all bytes consumed so far.
375 // Bytes [0..dataChunkDataPos-1] = WAV header.
376 // Bytes [dataChunkDataPos..m_headerBuf->size()-1] = PCM prefix already read.
377 // Trim the buffer to keep only the PCM prefix.
378 if (dataChunkDataPos < m_headerBuf->size()) {
379 *m_headerBuf = m_headerBuf->mid(qsizetype(dataChunkDataPos));
380 } else {
381 m_headerBuf->clear();
382 }
383 }
384
385 format.setSampleFormat(fmt);
386 format.setSampleRate(sampleRate);
387 format.setChannelCount(channels);
388
389 if (!dataSize)
390 dataSize = device->size() - dataChunkDataPos;
391
392 haveFormat = true;
393 disconnect(device, &QIODevice::readyRead, this, &QWaveDecoder::handleData);
394 connect(device, &QIODevice::readyRead, this, &QIODevice::readyRead);
395 emit formatKnown();
396}
397
398#endif // QT_DEPRECATED_SINCE(6, 11)
399
400QT_END_NAMESPACE
401
402#include "moc_qwavedecoder.cpp"