Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
4#include "qwavedecoder.h"
5
6#include <QtCore/qtimer.h>
7#include <QtCore/qendian.h>
8#include <limits.h>
9#include <qdebug.h>
10
12
13namespace {
14
15void bswap2(char *data, qsizetype count) noexcept
16{
17 for (qsizetype i = 0; i < count; ++i) {
18 qSwap(data[0], data[1]);
19 ++count;
20 data += 2;
21 }
22}
23
24void bswap4(char *data, qsizetype count) noexcept
25{
26 for (qsizetype i = 0; i < count; ++i) {
27 qSwap(data[0], data[3]);
28 qSwap(data[1], data[2]);
29 ++count;
30 data += 4;
31 }
32}
33
34}
35
41
48
50
51bool QWaveDecoder::open(QIODevice::OpenMode mode)
52{
53 bool canOpen = false;
56 if (canOpen && enoughDataAvailable())
57 handleData();
58 else
59 connect(device, &QIODevice::readyRead, this, &QWaveDecoder::handleData);
60 return canOpen;
61 }
62
64 if (format.sampleFormat() != QAudioFormat::Int16)
65 return false; // data format is not supported
66 canOpen = QIODevice::open(mode);
67 if (canOpen && writeHeader())
68 haveHeader = true;
69 return canOpen;
70 }
71 return QIODevice::open(mode);
72}
73
75{
76 if (isOpen() && (openMode() & QIODevice::WriteOnly)) {
77 Q_ASSERT(dataSize < INT_MAX);
78 if (!device->isOpen() || !writeDataLength())
79 qWarning() << "Failed to finalize wav file";
80 }
82}
83
85{
86 return device->seek(pos);
87}
88
90{
91 return device->pos();
92}
93
95{
96}
97
99{
100 return format;
101}
102
104{
105 return device;
106}
107
109{
111 return 0;
112 int bytesPerSec = format.bytesPerFrame() * format.sampleRate();
113 return bytesPerSec ? size() * 1000 / bytesPerSec : 0;
114}
115
117{
119 if (!haveFormat)
120 return 0;
121 if (bps == 24)
122 return dataSize*2/3;
123 return dataSize;
124 } else {
125 return device->size();
126 }
127}
128
130{
131 return device->isSequential();
132}
133
135{
136 return haveFormat ? device->bytesAvailable() : 0;
137}
138
140{
141 return HeaderLength;
142}
143
145{
146 const int bytesPerSample = format.bytesPerSample();
147 if (!haveFormat || bytesPerSample == 0)
148 return 0;
149
150 if (bps == 24) {
151 // 24 bit WAV, read in as 16 bit
152 qint64 l = 0;
153 while (l < maxlen - 1) {
154 char tmp[3];
155 device->read(tmp, 3);
156 if (byteSwap)
157 qSwap(tmp[0], tmp[2]);
158#if Q_BYTE_ORDER == Q_BIG_ENDIAN
159 data[0] = tmp[0];
160 data[1] = tmp[1];
161#else
162 data[0] = tmp[1];
163 data[1] = tmp[2];
164#endif
165 data += 2;
166 l += 2;
167 }
168 return l;
169 }
170
171 qint64 nSamples = maxlen / bytesPerSample;
172 maxlen = nSamples * bytesPerSample;
173 int read = device->read(data, maxlen);
174
175 if (!byteSwap || format.bytesPerFrame() == 1)
176 return read;
177
178 nSamples = read / bytesPerSample;
179 switch (bytesPerSample) {
180 case 2:
181 bswap2(data, nSamples);
182 break;
183 case 4:
184 bswap4(data, nSamples);
185 break;
186 default:
187 Q_UNREACHABLE();
188 }
189 return read;
190
191}
192
194{
195 if (!haveHeader)
196 return 0;
197 qint64 written = device->write(data, len);
198 dataSize += written;
199 return written;
200}
201
202bool QWaveDecoder::writeHeader()
203{
204 if (device->size() != 0)
205 return false;
206
207#ifndef Q_LITTLE_ENDIAN
208 // only implemented for LITTLE ENDIAN
209 return false;
210#endif
211
212 CombinedHeader header;
213
214 memset(&header, 0, HeaderLength);
215
216 // RIFF header
217 memcpy(header.riff.descriptor.id,"RIFF",4);
218 qToLittleEndian<quint32>(quint32(dataSize + HeaderLength - 8),
219 reinterpret_cast<unsigned char*>(&header.riff.descriptor.size));
220 memcpy(header.riff.type, "WAVE",4);
221
222 // WAVE header
223 memcpy(header.wave.descriptor.id,"fmt ",4);
224 qToLittleEndian<quint32>(quint32(16),
225 reinterpret_cast<unsigned char*>(&header.wave.descriptor.size));
226 qToLittleEndian<quint16>(quint16(1),
227 reinterpret_cast<unsigned char*>(&header.wave.audioFormat));
228 qToLittleEndian<quint16>(quint16(format.channelCount()),
229 reinterpret_cast<unsigned char*>(&header.wave.numChannels));
230 qToLittleEndian<quint32>(quint32(format.sampleRate()),
231 reinterpret_cast<unsigned char*>(&header.wave.sampleRate));
232 qToLittleEndian<quint32>(quint32(format.sampleRate() * format.bytesPerFrame()),
233 reinterpret_cast<unsigned char*>(&header.wave.byteRate));
234 qToLittleEndian<quint16>(quint16(format.channelCount() * format.bytesPerSample()),
235 reinterpret_cast<unsigned char*>(&header.wave.blockAlign));
236 qToLittleEndian<quint16>(quint16(format.bytesPerSample() * 8),
237 reinterpret_cast<unsigned char*>(&header.wave.bitsPerSample));
238
239 // DATA header
240 memcpy(header.data.descriptor.id,"data",4);
241 qToLittleEndian<quint32>(quint32(dataSize),
242 reinterpret_cast<unsigned char*>(&header.data.descriptor.size));
243
244 return device->write(reinterpret_cast<const char *>(&header), HeaderLength);
245}
246
247bool QWaveDecoder::writeDataLength()
248{
249#ifndef Q_LITTLE_ENDIAN
250 // only implemented for LITTLE ENDIAN
251 return false;
252#endif
253
254 if (isSequential())
255 return false;
256
257 // seek to RIFF header size, see header.riff.descriptor.size above
258 if (!device->seek(4)) {
259 qDebug() << "can't seek";
260 return false;
261 }
262
263 quint32 length = dataSize + HeaderLength - 8;
264 if (device->write(reinterpret_cast<const char *>(&length), 4) != 4)
265 return false;
266
267 // seek to DATA header size, see header.data.descriptor.size above
268 if (!device->seek(40))
269 return false;
270
271 return device->write(reinterpret_cast<const char *>(&dataSize), 4);
272}
273
274void QWaveDecoder::parsingFailed()
275{
276 Q_ASSERT(device);
277 disconnect(device, &QIODevice::readyRead, this, &QWaveDecoder::handleData);
279}
280
281void QWaveDecoder::handleData()
282{
284 return;
285
286 // As a special "state", if we have junk to skip, we do
287 if (junkToSkip > 0) {
288 discardBytes(junkToSkip); // this also updates junkToSkip
289
290 // If we couldn't skip all the junk, return
291 if (junkToSkip > 0) {
292 // We might have run out
293 if (device->atEnd())
294 parsingFailed();
295 return;
296 }
297 }
298
299 if (state == QWaveDecoder::InitialState) {
300 if (device->bytesAvailable() < qint64(sizeof(RIFFHeader)))
301 return;
302
303 RIFFHeader riff;
304 device->read(reinterpret_cast<char *>(&riff), sizeof(RIFFHeader));
305
306 // RIFF = little endian RIFF, RIFX = big endian RIFF
307 if (((qstrncmp(riff.descriptor.id, "RIFF", 4) != 0) && (qstrncmp(riff.descriptor.id, "RIFX", 4) != 0))
308 || qstrncmp(riff.type, "WAVE", 4) != 0) {
309 parsingFailed();
310 return;
311 }
312
313 state = QWaveDecoder::WaitingForFormatState;
314 bigEndian = (qstrncmp(riff.descriptor.id, "RIFX", 4) == 0);
315 byteSwap = (bigEndian != (QSysInfo::ByteOrder == QSysInfo::BigEndian));
316 }
317
318 if (state == QWaveDecoder::WaitingForFormatState) {
319 if (findChunk("fmt ")) {
320 chunk descriptor;
321 peekChunk(&descriptor);
322
323 quint32 rawChunkSize = descriptor.size + sizeof(chunk);
324 if (device->bytesAvailable() < qint64(rawChunkSize))
325 return;
326
327 WAVEHeader wave;
328 device->read(reinterpret_cast<char *>(&wave), sizeof(WAVEHeader));
329
330 if (rawChunkSize > sizeof(WAVEHeader))
331 discardBytes(rawChunkSize - sizeof(WAVEHeader));
332
333 // Swizzle this
334 if (bigEndian) {
335 wave.audioFormat = qFromBigEndian<quint16>(wave.audioFormat);
336 } else {
337 wave.audioFormat = qFromLittleEndian<quint16>(wave.audioFormat);
338 }
339
340 if (wave.audioFormat != 0 && wave.audioFormat != 1) {
341 // 32bit wave files have format == 0xFFFE (WAVE_FORMAT_EXTENSIBLE).
342 // but don't support them at the moment.
343 parsingFailed();
344 return;
345 }
346
347 int rate;
348 int channels;
349 if (bigEndian) {
350 bps = qFromBigEndian<quint16>(wave.bitsPerSample);
351 rate = qFromBigEndian<quint32>(wave.sampleRate);
352 channels = qFromBigEndian<quint16>(wave.numChannels);
353 } else {
354 bps = qFromLittleEndian<quint16>(wave.bitsPerSample);
355 rate = qFromLittleEndian<quint32>(wave.sampleRate);
356 channels = qFromLittleEndian<quint16>(wave.numChannels);
357 }
358
360 switch(bps) {
361 case 8:
363 break;
364 case 16:
366 break;
367 case 24:
369 break;
370 case 32:
372 break;
373 }
374 if (fmt == QAudioFormat::Unknown || rate == 0 || channels == 0) {
375 parsingFailed();
376 return;
377 }
378
379 format.setSampleFormat(fmt);
380 format.setSampleRate(rate);
381 format.setChannelCount(channels);
382
383 state = QWaveDecoder::WaitingForDataState;
384 }
385 }
386
387 if (state == QWaveDecoder::WaitingForDataState) {
388 if (findChunk("data")) {
389 disconnect(device, &QIODevice::readyRead, this, &QWaveDecoder::handleData);
390
391 chunk descriptor;
392 device->read(reinterpret_cast<char *>(&descriptor), sizeof(chunk));
393 if (bigEndian)
394 descriptor.size = qFromBigEndian<quint32>(descriptor.size);
395 else
396 descriptor.size = qFromLittleEndian<quint32>(descriptor.size);
397
398 dataSize = descriptor.size; //means the data size from the data header, not the actual file size
399 if (!dataSize)
400 dataSize = device->size() - headerLength();
401
402 haveFormat = true;
405
406 return;
407 }
408 }
409
410 // If we hit the end without finding data, it's a parsing error
411 if (device->atEnd()) {
412 parsingFailed();
413 }
414}
415
416bool QWaveDecoder::enoughDataAvailable()
417{
418 chunk descriptor;
419 if (!peekChunk(&descriptor, false))
420 return false;
421
422 // This is only called for the RIFF/RIFX header, before bigEndian is set,
423 // so we have to manually swizzle
424 if (qstrncmp(descriptor.id, "RIFX", 4) == 0)
425 descriptor.size = qFromBigEndian<quint32>(descriptor.size);
426 if (qstrncmp(descriptor.id, "RIFF", 4) == 0)
427 descriptor.size = qFromLittleEndian<quint32>(descriptor.size);
428
429 if (device->bytesAvailable() < qint64(sizeof(chunk) + descriptor.size))
430 return false;
431
432 return true;
433}
434
435bool QWaveDecoder::findChunk(const char *chunkId)
436{
437 chunk descriptor;
438
439 do {
440 if (!peekChunk(&descriptor))
441 return false;
442
443 if (qstrncmp(descriptor.id, chunkId, 4) == 0)
444 return true;
445
446 // A program reading a RIFF file can skip over any chunk whose chunk
447 // ID it doesn't recognize; it simply skips the number of bytes specified
448 // by ckSize plus the pad byte, if present. See Multimedia Programming
449 // Interface and Data Specifications 1.0. IBM / Microsoft. August 1991. pp. 10-11.
450 const quint32 sizeWithPad = descriptor.size + (descriptor.size & 1);
451
452 // It's possible that bytes->available() is less than the chunk size
453 // if it's corrupt.
454 junkToSkip = qint64(sizeof(chunk) + sizeWithPad);
455
456 // Skip the current amount
457 if (junkToSkip > 0)
458 discardBytes(junkToSkip);
459
460 // If we still have stuff left, just exit and try again later
461 // since we can't call peekChunk
462 if (junkToSkip > 0)
463 return false;
464
465 } while (device->bytesAvailable() > 0);
466
467 return false;
468}
469
470bool QWaveDecoder::peekChunk(chunk *pChunk, bool handleEndianness)
471{
472 if (device->bytesAvailable() < qint64(sizeof(chunk)))
473 return false;
474
475 if (!device->peek(reinterpret_cast<char *>(pChunk), sizeof(chunk)))
476 return false;
477
478 if (handleEndianness) {
479 if (bigEndian)
480 pChunk->size = qFromBigEndian<quint32>(pChunk->size);
481 else
482 pChunk->size = qFromLittleEndian<quint32>(pChunk->size);
483 }
484 return true;
485}
486
487void QWaveDecoder::discardBytes(qint64 numBytes)
488{
489 // Discards a number of bytes
490 // If the iodevice doesn't have this many bytes in it,
491 // remember how much more junk we have to skip.
492 if (device->isSequential()) {
493 QByteArray r = device->read(qMin(numBytes, qint64(16384))); // uggh, wasted memory, limit to a max of 16k
494 if (r.size() < numBytes)
495 junkToSkip = numBytes - r.size();
496 else
497 junkToSkip = 0;
498 } else {
499 quint64 origPos = device->pos();
500 device->seek(device->pos() + numBytes);
501 junkToSkip = origPos + numBytes - device->pos();
502 }
503}
504
506
507#include "moc_qwavedecoder.cpp"
IOBluetoothDevice * device
The QAudioFormat class stores audio stream parameter information.
SampleFormat
Qt will always expect and use samples in the endianness of the host platform.
\inmodule QtCore
Definition qbytearray.h:57
\inmodule QtCore \reentrant
Definition qiodevice.h:34
virtual bool open(QIODeviceBase::OpenMode mode)
Opens the device and sets its OpenMode to mode.
void readyRead()
This signal is emitted once every time new data is available for reading from the device's current re...
virtual qint64 size() const
For open random-access devices, this function returns the size of the device.
virtual qint64 pos() const
For random-access devices, this function returns the position that data is written to or read from.
virtual bool isSequential() const
Returns true if this device is sequential; otherwise returns false.
bool isOpen() const
Returns true if the device is open; otherwise returns false.
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
QIODeviceBase::OpenMode openMode() const
Returns the mode in which the device has been opened; i.e.
qint64 peek(char *data, qint64 maxlen)
virtual qint64 bytesAvailable() const
Returns the number of bytes that are available for reading.
virtual void close()
First emits aboutToClose(), then closes the device and sets its OpenMode to NotOpen.
virtual bool seek(qint64 pos)
For random-access devices, this function sets the current position to pos, returning true on success,...
virtual bool atEnd() const
Returns true if the current read and write position is at the end of the device (i....
qint64 read(char *data, qint64 maxlen)
Reads at most maxSize bytes from the device into data, and returns the number of bytes read.
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
@ BigEndian
Definition qsysinfo.h:29
@ ByteOrder
Definition qsysinfo.h:34
qint64 bytesAvailable() const override
Returns the number of bytes that are available for reading.
static qint64 headerLength()
qint64 readData(char *data, qint64 maxlen) override
Reads up to maxSize bytes from the device into data, and returns the number of bytes read or -1 if an...
qint64 size() const override
For open random-access devices, this function returns the size of the device.
QIODevice * getDevice()
int duration() const
qint64 pos() const override
For random-access devices, this function returns the position that data is written to or read from.
void formatKnown()
qint64 writeData(const char *data, qint64 len) override
Writes up to maxSize bytes from data to the device.
QAudioFormat audioFormat() const
bool seek(qint64 pos) override
For random-access devices, this function sets the current position to pos, returning true on success,...
bool open(QIODevice::OpenMode mode) override
void parsingError()
void setIODevice(QIODevice *device)
bool isSequential() const override
Returns true if this device is sequential; otherwise returns false.
void close() override
First emits aboutToClose(), then closes the device and sets its OpenMode to NotOpen.
QWaveDecoder(QIODevice *device, QObject *parent=nullptr)
Combined button and popup list for selecting options.
void bswap4(char *data, qsizetype count) noexcept
void bswap2(char *data, qsizetype count) noexcept
int qstrncmp(const char *str1, const char *str2, size_t len)
static QString header(const QString &name)
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
GLenum mode
GLboolean r
[2]
GLenum GLsizei dataSize
GLenum GLuint GLenum GLsizei length
GLenum GLenum GLsizei count
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLint GLsizei GLsizei GLenum format
GLuint GLenum * rate
GLenum GLsizei len
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QT_BEGIN_NAMESPACE constexpr void qSwap(T &value1, T &value2) noexcept(std::is_nothrow_swappable_v< T >)
Definition qswap.h:20
#define emit
unsigned int quint32
Definition qtypes.h:50
unsigned short quint16
Definition qtypes.h:48
unsigned long long quint64
Definition qtypes.h:61
ptrdiff_t qsizetype
Definition qtypes.h:165
long long qint64
Definition qtypes.h:60
QVideoFrameFormat::PixelFormat fmt
myObject disconnect()
[26]