1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QtCore/qendian.h>
30#include "wavheader.h"
31
32
33struct chunk
34{
35 char id[4];
36 quint32 size;
37};
38
39struct RIFFHeader
40{
41 chunk descriptor; // "RIFF"
42 char type[4]; // "WAVE"
43};
44
45struct WAVEHeader
46{
47 chunk descriptor;
48 quint16 audioFormat;
49 quint16 numChannels;
50 quint32 sampleRate;
51 quint32 byteRate;
52 quint16 blockAlign;
53 quint16 bitsPerSample;
54};
55
56struct DATAHeader
57{
58 chunk descriptor;
59};
60
61struct CombinedHeader
62{
63 RIFFHeader riff;
64 WAVEHeader wave;
65 DATAHeader data;
66};
67
68static const int HeaderLength = sizeof(CombinedHeader);
69
70
71WavHeader::WavHeader(const QAudioFormat &format, qint64 dataLength)
72 : m_format(format)
73 , m_dataLength(dataLength)
74{
75
76}
77
78bool WavHeader::read(QIODevice &device)
79{
80 bool result = true;
81
82 if (!device.isSequential())
83 result = device.seek(pos: 0);
84 // else, assume that current position is the start of the header
85
86 if (result) {
87 CombinedHeader header;
88 result = (device.read(data: reinterpret_cast<char *>(&header), maxlen: HeaderLength) == HeaderLength);
89 if (result) {
90 if ((memcmp(s1: &header.riff.descriptor.id, s2: "RIFF", n: 4) == 0
91 || memcmp(s1: &header.riff.descriptor.id, s2: "RIFX", n: 4) == 0)
92 && memcmp(s1: &header.riff.type, s2: "WAVE", n: 4) == 0
93 && memcmp(s1: &header.wave.descriptor.id, s2: "fmt ", n: 4) == 0
94 && header.wave.audioFormat == 1 // PCM
95 ) {
96 if (memcmp(s1: &header.riff.descriptor.id, s2: "RIFF", n: 4) == 0)
97 m_format.setByteOrder(QAudioFormat::LittleEndian);
98 else
99 m_format.setByteOrder(QAudioFormat::BigEndian);
100
101 m_format.setChannelCount(qFromLittleEndian<quint16>(source: header.wave.numChannels));
102 m_format.setCodec("audio/pcm");
103 m_format.setSampleRate(qFromLittleEndian<quint32>(source: header.wave.sampleRate));
104 m_format.setSampleSize(qFromLittleEndian<quint16>(source: header.wave.bitsPerSample));
105
106 switch(header.wave.bitsPerSample) {
107 case 8:
108 m_format.setSampleType(QAudioFormat::UnSignedInt);
109 break;
110 case 16:
111 m_format.setSampleType(QAudioFormat::SignedInt);
112 break;
113 default:
114 result = false;
115 }
116
117 m_dataLength = device.size() - HeaderLength;
118 } else {
119 result = false;
120 }
121 }
122 }
123
124 return result;
125}
126
127bool WavHeader::write(QIODevice &device)
128{
129 CombinedHeader header;
130
131 memset(s: &header, c: 0, n: HeaderLength);
132
133 // RIFF header
134 if (m_format.byteOrder() == QAudioFormat::LittleEndian)
135 memcpy(dest: header.riff.descriptor.id,src: "RIFF",n: 4);
136 else
137 memcpy(dest: header.riff.descriptor.id,src: "RIFX",n: 4);
138 qToLittleEndian<quint32>(src: quint32(m_dataLength + HeaderLength - 8),
139 dest: reinterpret_cast<unsigned char*>(&header.riff.descriptor.size));
140 memcpy(dest: header.riff.type, src: "WAVE",n: 4);
141
142 // WAVE header
143 memcpy(dest: header.wave.descriptor.id,src: "fmt ",n: 4);
144 qToLittleEndian<quint32>(src: quint32(16),
145 dest: reinterpret_cast<unsigned char*>(&header.wave.descriptor.size));
146 qToLittleEndian<quint16>(src: quint16(1),
147 dest: reinterpret_cast<unsigned char*>(&header.wave.audioFormat));
148 qToLittleEndian<quint16>(src: quint16(m_format.channelCount()),
149 dest: reinterpret_cast<unsigned char*>(&header.wave.numChannels));
150 qToLittleEndian<quint32>(src: quint32(m_format.sampleRate()),
151 dest: reinterpret_cast<unsigned char*>(&header.wave.sampleRate));
152 qToLittleEndian<quint32>(src: quint32(m_format.sampleRate() * m_format.channelCount() * m_format.sampleSize() / 8),
153 dest: reinterpret_cast<unsigned char*>(&header.wave.byteRate));
154 qToLittleEndian<quint16>(src: quint16(m_format.channelCount() * m_format.sampleSize() / 8),
155 dest: reinterpret_cast<unsigned char*>(&header.wave.blockAlign));
156 qToLittleEndian<quint16>(src: quint16(m_format.sampleSize()),
157 dest: reinterpret_cast<unsigned char*>(&header.wave.bitsPerSample));
158
159 // DATA header
160 memcpy(dest: header.data.descriptor.id,src: "data",n: 4);
161 qToLittleEndian<quint32>(src: quint32(m_dataLength),
162 dest: reinterpret_cast<unsigned char*>(&header.data.descriptor.size));
163
164 return (device.write(data: reinterpret_cast<const char *>(&header), len: HeaderLength) == HeaderLength);
165}
166
167const QAudioFormat& WavHeader::format() const
168{
169 return m_format;
170}
171
172qint64 WavHeader::dataLength() const
173{
174 return m_dataLength;
175}
176
177qint64 WavHeader::headerLength()
178{
179 return HeaderLength;
180}
181
182bool WavHeader::writeDataLength(QIODevice &device, qint64 dataLength)
183{
184 bool result = false;
185 if (!device.isSequential()) {
186 device.seek(pos: 40);
187 unsigned char dataLengthLE[4];
188 qToLittleEndian<quint32>(src: quint32(dataLength), dest: dataLengthLE);
189 result = (device.write(data: reinterpret_cast<const char *>(dataLengthLE), len: 4) == 4);
190 }
191 return result;
192}
193

source code of qtmultimedia/tests/auto/integration/qaudiooutput/wavheader.cpp