Qt 6.x
The Qt SDK
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
39{
40}
41
46{
47}
48
50
51bool QWaveDecoder::open(QIODevice::OpenMode mode)
52{
53 bool canOpen = false;
56 if (canOpen && enoughDataAvailable())
57 handleData();
58 else
59 connect(device, SIGNAL(readyRead()), SLOT(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 return format;
97}
98
100{
101 return device;
102}
103
105{
107 return 0;
108 int bytesPerSec = format.bytesPerFrame() * format.sampleRate();
109 return bytesPerSec ? size() * 1000 / bytesPerSec : 0;
110}
111
113{
115 if (!haveFormat)
116 return 0;
117 if (bps == 24)
118 return dataSize*2/3;
119 return dataSize;
120 } else {
121 return device->size();
122 }
123}
124
126{
127 return device->isSequential();
128}
129
131{
132 return haveFormat ? device->bytesAvailable() : 0;
133}
134
136{
137 return HeaderLength;
138}
139
141{
142 const int bytesPerSample = format.bytesPerSample();
143 if (!haveFormat || bytesPerSample == 0)
144 return 0;
145
146 if (bps == 24) {
147 // 24 bit WAV, read in as 16 bit
148 qint64 l = 0;
149 while (l < maxlen - 1) {
150 char tmp[3];
151 device->read(tmp, 3);
152 if (byteSwap)
153 qSwap(tmp[0], tmp[2]);
154#if Q_BYTE_ORDER == Q_BIG_ENDIAN
155 data[0] = tmp[0];
156 data[1] = tmp[1];
157#else
158 data[0] = tmp[1];
159 data[1] = tmp[2];
160#endif
161 data += 2;
162 l += 2;
163 }
164 return l;
165 }
166
167 qint64 nSamples = maxlen / bytesPerSample;
168 maxlen = nSamples * bytesPerSample;
169 int read = device->read(data, maxlen);
170
171 if (!byteSwap || format.bytesPerFrame() == 1)
172 return read;
173
174 nSamples = read / bytesPerSample;
175 switch (bytesPerSample) {
176 case 2:
177 bswap2(data, nSamples);
178 break;
179 case 4:
180 bswap4(data, nSamples);
181 break;
182 default:
183 Q_UNREACHABLE();
184 }
185 return read;
186
187}
188
190{
191 if (!haveHeader)
192 return 0;
193 qint64 written = device->write(data, len);
194 dataSize += written;
195 return written;
196}
197
198bool QWaveDecoder::writeHeader()
199{
200 if (device->size() != 0)
201 return false;
202
203#ifndef Q_LITTLE_ENDIAN
204 // only implemented for LITTLE ENDIAN
205 return false;
206#endif
207
208 CombinedHeader header;
209
210 memset(&header, 0, HeaderLength);
211
212 // RIFF header
213 memcpy(header.riff.descriptor.id,"RIFF",4);
214 qToLittleEndian<quint32>(quint32(dataSize + HeaderLength - 8),
215 reinterpret_cast<unsigned char*>(&header.riff.descriptor.size));
216 memcpy(header.riff.type, "WAVE",4);
217
218 // WAVE header
219 memcpy(header.wave.descriptor.id,"fmt ",4);
220 qToLittleEndian<quint32>(quint32(16),
221 reinterpret_cast<unsigned char*>(&header.wave.descriptor.size));
222 qToLittleEndian<quint16>(quint16(1),
223 reinterpret_cast<unsigned char*>(&header.wave.audioFormat));
224 qToLittleEndian<quint16>(quint16(format.channelCount()),
225 reinterpret_cast<unsigned char*>(&header.wave.numChannels));
226 qToLittleEndian<quint32>(quint32(format.sampleRate()),
227 reinterpret_cast<unsigned char*>(&header.wave.sampleRate));
228 qToLittleEndian<quint32>(quint32(format.sampleRate() * format.bytesPerFrame()),
229 reinterpret_cast<unsigned char*>(&header.wave.byteRate));
230 qToLittleEndian<quint16>(quint16(format.channelCount() * format.bytesPerSample()),
231 reinterpret_cast<unsigned char*>(&header.wave.blockAlign));
232 qToLittleEndian<quint16>(quint16(format.bytesPerSample() * 8),
233 reinterpret_cast<unsigned char*>(&header.wave.bitsPerSample));
234
235 // DATA header
236 memcpy(header.data.descriptor.id,"data",4);
237 qToLittleEndian<quint32>(quint32(dataSize),
238 reinterpret_cast<unsigned char*>(&header.data.descriptor.size));
239
240 return device->write(reinterpret_cast<const char *>(&header), HeaderLength);
241}
242
243bool QWaveDecoder::writeDataLength()
244{
245#ifndef Q_LITTLE_ENDIAN
246 // only implemented for LITTLE ENDIAN
247 return false;
248#endif
249
250 if (isSequential())
251 return false;
252
253 // seek to RIFF header size, see header.riff.descriptor.size above
254 if (!device->seek(4)) {
255 qDebug() << "can't seek";
256 return false;
257 }
258
259 quint32 length = dataSize + HeaderLength - 8;
260 if (device->write(reinterpret_cast<const char *>(&length), 4) != 4)
261 return false;
262
263 // seek to DATA header size, see header.data.descriptor.size above
264 if (!device->seek(40))
265 return false;
266
267 return device->write(reinterpret_cast<const char *>(&dataSize), 4);
268}
269
270void QWaveDecoder::parsingFailed()
271{
272 Q_ASSERT(device);
273 device->disconnect(SIGNAL(readyRead()), this, SLOT(handleData()));
275}
276
277void QWaveDecoder::handleData()
278{
280 return;
281
282 // As a special "state", if we have junk to skip, we do
283 if (junkToSkip > 0) {
284 discardBytes(junkToSkip); // this also updates junkToSkip
285
286 // If we couldn't skip all the junk, return
287 if (junkToSkip > 0) {
288 // We might have run out
289 if (device->atEnd())
290 parsingFailed();
291 return;
292 }
293 }
294
295 if (state == QWaveDecoder::InitialState) {
296 if (device->bytesAvailable() < qint64(sizeof(RIFFHeader)))
297 return;
298
299 RIFFHeader riff;
300 device->read(reinterpret_cast<char *>(&riff), sizeof(RIFFHeader));
301
302 // RIFF = little endian RIFF, RIFX = big endian RIFF
303 if (((qstrncmp(riff.descriptor.id, "RIFF", 4) != 0) && (qstrncmp(riff.descriptor.id, "RIFX", 4) != 0))
304 || qstrncmp(riff.type, "WAVE", 4) != 0) {
305 parsingFailed();
306 return;
307 }
308
309 state = QWaveDecoder::WaitingForFormatState;
310 bigEndian = (qstrncmp(riff.descriptor.id, "RIFX", 4) == 0);
311 byteSwap = (bigEndian != (QSysInfo::ByteOrder == QSysInfo::BigEndian));
312 }
313
314 if (state == QWaveDecoder::WaitingForFormatState) {
315 if (findChunk("fmt ")) {
316 chunk descriptor;
317 peekChunk(&descriptor);
318
319 quint32 rawChunkSize = descriptor.size + sizeof(chunk);
320 if (device->bytesAvailable() < qint64(rawChunkSize))
321 return;
322
323 WAVEHeader wave;
324 device->read(reinterpret_cast<char *>(&wave), sizeof(WAVEHeader));
325
326 if (rawChunkSize > sizeof(WAVEHeader))
327 discardBytes(rawChunkSize - sizeof(WAVEHeader));
328
329 // Swizzle this
330 if (bigEndian) {
331 wave.audioFormat = qFromBigEndian<quint16>(wave.audioFormat);
332 } else {
333 wave.audioFormat = qFromLittleEndian<quint16>(wave.audioFormat);
334 }
335
336 if (wave.audioFormat != 0 && wave.audioFormat != 1) {
337 // 32bit wave files have format == 0xFFFE (WAVE_FORMAT_EXTENSIBLE).
338 // but don't support them at the moment.
339 parsingFailed();
340 return;
341 }
342
343 int rate;
344 int channels;
345 if (bigEndian) {
346 bps = qFromBigEndian<quint16>(wave.bitsPerSample);
347 rate = qFromBigEndian<quint32>(wave.sampleRate);
348 channels = qFromBigEndian<quint16>(wave.numChannels);
349 } else {
350 bps = qFromLittleEndian<quint16>(wave.bitsPerSample);
351 rate = qFromLittleEndian<quint32>(wave.sampleRate);
352 channels = qFromLittleEndian<quint16>(wave.numChannels);
353 }
354
356 switch(bps) {
357 case 8:
359 break;
360 case 16:
362 break;
363 case 24:
365 break;
366 case 32:
368 break;
369 }
370 if (fmt == QAudioFormat::Unknown || rate == 0 || channels == 0) {
371 parsingFailed();
372 return;
373 }
374
375 format.setSampleFormat(fmt);
376 format.setSampleRate(/*qFromBigEndian<quint32>*/(wave.sampleRate));
377 format.setChannelCount(/*qFromBigEndian<quint16>*/(wave.numChannels));
378
379 state = QWaveDecoder::WaitingForDataState;
380 }
381 }
382
383 if (state == QWaveDecoder::WaitingForDataState) {
384 if (findChunk("data")) {
385 device->disconnect(SIGNAL(readyRead()), this, SLOT(handleData()));
386
387 chunk descriptor;
388 device->read(reinterpret_cast<char *>(&descriptor), sizeof(chunk));
389 if (bigEndian)
390 descriptor.size = qFromBigEndian<quint32>(descriptor.size);
391 else
392 descriptor.size = qFromLittleEndian<quint32>(descriptor.size);
393
394 dataSize = descriptor.size; //means the data size from the data header, not the actual file size
395 if (!dataSize)
396 dataSize = device->size() - headerLength();
397
398 haveFormat = true;
399 connect(device, SIGNAL(readyRead()), SIGNAL(readyRead()));
401
402 return;
403 }
404 }
405
406 // If we hit the end without finding data, it's a parsing error
407 if (device->atEnd()) {
408 parsingFailed();
409 }
410}
411
412bool QWaveDecoder::enoughDataAvailable()
413{
414 chunk descriptor;
415 if (!peekChunk(&descriptor, false))
416 return false;
417
418 // This is only called for the RIFF/RIFX header, before bigEndian is set,
419 // so we have to manually swizzle
420 if (qstrncmp(descriptor.id, "RIFX", 4) == 0)
421 descriptor.size = qFromBigEndian<quint32>(descriptor.size);
422 if (qstrncmp(descriptor.id, "RIFF", 4) == 0)
423 descriptor.size = qFromLittleEndian<quint32>(descriptor.size);
424
425 if (device->bytesAvailable() < qint64(sizeof(chunk) + descriptor.size))
426 return false;
427
428 return true;
429}
430
431bool QWaveDecoder::findChunk(const char *chunkId)
432{
433 chunk descriptor;
434
435 do {
436 if (!peekChunk(&descriptor))
437 return false;
438
439 if (qstrncmp(descriptor.id, chunkId, 4) == 0)
440 return true;
441
442 // It's possible that bytes->available() is less than the chunk size
443 // if it's corrupt.
444 junkToSkip = qint64(sizeof(chunk) + descriptor.size);
445
446 // Skip the current amount
447 if (junkToSkip > 0)
448 discardBytes(junkToSkip);
449
450 // If we still have stuff left, just exit and try again later
451 // since we can't call peekChunk
452 if (junkToSkip > 0)
453 return false;
454
455 } while (device->bytesAvailable() > 0);
456
457 return false;
458}
459
460bool QWaveDecoder::peekChunk(chunk *pChunk, bool handleEndianness)
461{
462 if (device->bytesAvailable() < qint64(sizeof(chunk)))
463 return false;
464
465 if (!device->peek(reinterpret_cast<char *>(pChunk), sizeof(chunk)))
466 return false;
467
468 if (handleEndianness) {
469 if (bigEndian)
470 pChunk->size = qFromBigEndian<quint32>(pChunk->size);
471 else
472 pChunk->size = qFromLittleEndian<quint32>(pChunk->size);
473 }
474 return true;
475}
476
477void QWaveDecoder::discardBytes(qint64 numBytes)
478{
479 // Discards a number of bytes
480 // If the iodevice doesn't have this many bytes in it,
481 // remember how much more junk we have to skip.
482 if (device->isSequential()) {
483 QByteArray r = device->read(qMin(numBytes, qint64(16384))); // uggh, wasted memory, limit to a max of 16k
484 if (r.size() < numBytes)
485 junkToSkip = numBytes - r.size();
486 else
487 junkToSkip = 0;
488 } else {
489 quint64 origPos = device->pos();
490 device->seek(device->pos() + numBytes);
491 junkToSkip = origPos + numBytes - device->pos();
492 }
493}
494
496
497#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:90
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3099
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
QChar * data()
Returns a pointer to the data stored in the QString.
Definition qstring.h:1095
@ 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()
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)
qSwap(pi, e)
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:160
#define qWarning
Definition qlogging.h:162
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
#define SLOT(a)
Definition qobjectdefs.h:51
#define SIGNAL(a)
Definition qobjectdefs.h:52
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
#define emit
unsigned int quint32
Definition qtypes.h:45
unsigned short quint16
Definition qtypes.h:43
unsigned long long quint64
Definition qtypes.h:56
ptrdiff_t qsizetype
Definition qtypes.h:70
long long qint64
Definition qtypes.h:55
QVideoFrameFormat::PixelFormat fmt
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent