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
qcoreaudioutils.mm
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5#include <qdebug.h>
6#include <mach/mach_time.h>
7
9
10double CoreAudioUtils::sFrequency = 0.0;
11bool CoreAudioUtils::sIsInitialized = false;
12
13void CoreAudioUtils::initialize()
14{
15 struct mach_timebase_info timeBaseInfo;
16 mach_timebase_info(&timeBaseInfo);
17 sFrequency = static_cast<double>(timeBaseInfo.denom) / static_cast<double>(timeBaseInfo.numer);
18 sFrequency *= 1000000000.0;
19
20 sIsInitialized = true;
21}
22
23
24quint64 CoreAudioUtils::currentTime()
25{
26 return mach_absolute_time();
27}
28
30{
31 if (!sIsInitialized)
32 initialize();
33 return sFrequency;
34}
35
36QAudioFormat CoreAudioUtils::toQAudioFormat(AudioStreamBasicDescription const& sf)
37{
38 QAudioFormat audioFormat;
39 // all Darwin HW is little endian, we ignore those formats
40 if ((sf.mFormatFlags & kAudioFormatFlagIsBigEndian) != 0 && QSysInfo::ByteOrder != QSysInfo::LittleEndian)
41 return audioFormat;
42
43 // filter out the formats we're interested in
44 QAudioFormat::SampleFormat format = QAudioFormat::Unknown;
45 switch (sf.mBitsPerChannel) {
46 case 8:
47 if ((sf.mFormatFlags & kAudioFormatFlagIsSignedInteger) == 0)
48 format = QAudioFormat::UInt8;
49 break;
50 case 16:
51 if ((sf.mFormatFlags & kAudioFormatFlagIsSignedInteger) != 0)
52 format = QAudioFormat::Int16;
53 break;
54 case 32:
55 if ((sf.mFormatFlags & kAudioFormatFlagIsSignedInteger) != 0)
56 format = QAudioFormat::Int32;
57 else if ((sf.mFormatFlags & kAudioFormatFlagIsFloat) != 0)
58 format = QAudioFormat::Float;
59 break;
60 default:
61 break;
62 }
63
64 audioFormat.setSampleFormat(format);
65 audioFormat.setSampleRate(sf.mSampleRate);
66 audioFormat.setChannelCount(sf.mChannelsPerFrame);
67
68 return audioFormat;
69}
70
71AudioStreamBasicDescription CoreAudioUtils::toAudioStreamBasicDescription(QAudioFormat const& audioFormat)
72{
73 AudioStreamBasicDescription sf;
74
75 sf.mFormatFlags = kAudioFormatFlagIsPacked;
76 sf.mSampleRate = audioFormat.sampleRate();
77 sf.mFramesPerPacket = 1;
78 sf.mChannelsPerFrame = audioFormat.channelCount();
79 sf.mBitsPerChannel = audioFormat.bytesPerSample() * 8;
80 sf.mBytesPerFrame = audioFormat.bytesPerFrame();
81 sf.mBytesPerPacket = sf.mFramesPerPacket * sf.mBytesPerFrame;
82 sf.mFormatID = kAudioFormatLinearPCM;
83
84 switch (audioFormat.sampleFormat()) {
85 case QAudioFormat::Int16:
86 case QAudioFormat::Int32:
87 sf.mFormatFlags |= kAudioFormatFlagIsSignedInteger;
88 break;
89 case QAudioFormat::Float:
90 sf.mFormatFlags |= kAudioFormatFlagIsFloat;
91 break;
92 case QAudioFormat::UInt8:
93 /* default */
94 case QAudioFormat::Unknown:
95 case QAudioFormat::NSampleFormats:
96 break;
97 }
98
99 return sf;
100}
101
102
103static constexpr struct {
106} channelMap[] = {
107 { QAudioFormat::FrontLeft, kAudioChannelLabel_Left },
108 { QAudioFormat::FrontRight, kAudioChannelLabel_Right },
109 { QAudioFormat::FrontCenter, kAudioChannelLabel_Center },
110 { QAudioFormat::LFE, kAudioChannelLabel_LFEScreen },
111 { QAudioFormat::BackLeft, kAudioChannelLabel_LeftSurround },
112 { QAudioFormat::BackRight, kAudioChannelLabel_RightSurround },
113 { QAudioFormat::FrontLeftOfCenter, kAudioChannelLabel_LeftCenter },
114 { QAudioFormat::FrontRightOfCenter, kAudioChannelLabel_RightCenter },
115 { QAudioFormat::BackCenter, kAudioChannelLabel_CenterSurround },
116 { QAudioFormat::LFE2, kAudioChannelLabel_LFE2 },
117 { QAudioFormat::SideLeft, kAudioChannelLabel_LeftSurroundDirect }, // ???
118 { QAudioFormat::SideRight, kAudioChannelLabel_RightSurroundDirect }, // ???
119 { QAudioFormat::TopFrontLeft, kAudioChannelLabel_VerticalHeightLeft },
120 { QAudioFormat::TopFrontRight, kAudioChannelLabel_VerticalHeightRight },
121 { QAudioFormat::TopFrontCenter, kAudioChannelLabel_VerticalHeightCenter },
122 { QAudioFormat::TopCenter, kAudioChannelLabel_CenterTopMiddle },
123 { QAudioFormat::TopBackLeft, kAudioChannelLabel_TopBackLeft },
124 { QAudioFormat::TopBackRight, kAudioChannelLabel_TopBackRight },
125 { QAudioFormat::TopSideLeft, kAudioChannelLabel_LeftTopMiddle },
126 { QAudioFormat::TopSideRight, kAudioChannelLabel_RightTopMiddle },
127 { QAudioFormat::TopBackCenter, kAudioChannelLabel_TopBackCenter },
129
130std::unique_ptr<AudioChannelLayout> CoreAudioUtils::toAudioChannelLayout(const QAudioFormat &format, UInt32 *size)
131{
132 auto channelConfig = format.channelConfig();
133 if (channelConfig == QAudioFormat::ChannelConfigUnknown)
134 channelConfig = QAudioFormat::defaultChannelConfigForChannelCount(format.channelCount());
135
136 *size = sizeof(AudioChannelLayout) + int(QAudioFormat::NChannelPositions)*sizeof(AudioChannelDescription);
137 auto *layout = static_cast<AudioChannelLayout *>(malloc(*size));
138 memset(layout, 0, *size);
139 layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
140
141 for (const auto &m : channelMap) {
142 if (channelConfig & QAudioFormat::channelConfig(m.pos))
143 layout->mChannelDescriptions[layout->mNumberChannelDescriptions++].mChannelLabel = m.label;
144 }
145
146 if (channelConfig & QAudioFormat::channelConfig(QAudioFormat::BottomFrontCenter)) {
147 auto &desc = layout->mChannelDescriptions[layout->mNumberChannelDescriptions++];
148 desc.mChannelLabel = kAudioChannelLabel_UseCoordinates;
149 desc.mChannelFlags = kAudioChannelFlags_SphericalCoordinates;
150 desc.mCoordinates[kAudioChannelCoordinates_Azimuth] = 0.f;
151 desc.mCoordinates[kAudioChannelCoordinates_Elevation] = -20.;
152 desc.mCoordinates[kAudioChannelCoordinates_Distance] = 1.f;
153 }
154 if (channelConfig & QAudioFormat::channelConfig(QAudioFormat::BottomFrontLeft)) {
155 auto &desc = layout->mChannelDescriptions[layout->mNumberChannelDescriptions++];
156 desc.mChannelLabel = kAudioChannelLabel_UseCoordinates;
157 desc.mChannelFlags = kAudioChannelFlags_SphericalCoordinates;
158 desc.mCoordinates[kAudioChannelCoordinates_Azimuth] = -45.f;
159 desc.mCoordinates[kAudioChannelCoordinates_Elevation] = -20.;
160 desc.mCoordinates[kAudioChannelCoordinates_Distance] = 1.f;
161 }
162 if (channelConfig & QAudioFormat::channelConfig(QAudioFormat::BottomFrontRight)) {
163 auto &desc = layout->mChannelDescriptions[layout->mNumberChannelDescriptions++];
164 desc.mChannelLabel = kAudioChannelLabel_UseCoordinates;
165 desc.mChannelFlags = kAudioChannelFlags_SphericalCoordinates;
166 desc.mCoordinates[kAudioChannelCoordinates_Azimuth] = 45.f;
167 desc.mCoordinates[kAudioChannelCoordinates_Elevation] = -20.;
168 desc.mCoordinates[kAudioChannelCoordinates_Distance] = 1.f;
169 }
170
171 return std::unique_ptr<AudioChannelLayout>(layout);
172}
173
174static constexpr struct {
177} layoutTagMap[] = {
178 { kAudioChannelLayoutTag_Mono, QAudioFormat::ChannelConfigMono },
179 { kAudioChannelLayoutTag_Stereo, QAudioFormat::ChannelConfigStereo },
180 { kAudioChannelLayoutTag_StereoHeadphones, QAudioFormat::ChannelConfigStereo },
181 { kAudioChannelLayoutTag_MPEG_1_0, QAudioFormat::ChannelConfigMono },
182 { kAudioChannelLayoutTag_MPEG_2_0, QAudioFormat::ChannelConfigStereo },
183 { kAudioChannelLayoutTag_MPEG_3_0_A, QAudioFormat::channelConfig(QAudioFormat::FrontLeft,
184 QAudioFormat::FrontRight,
185 QAudioFormat::FrontCenter) },
186 { kAudioChannelLayoutTag_MPEG_4_0_A, QAudioFormat::channelConfig(QAudioFormat::FrontLeft,
187 QAudioFormat::FrontRight,
188 QAudioFormat::FrontCenter,
189 QAudioFormat::BackCenter) },
190 { kAudioChannelLayoutTag_MPEG_5_0_A, QAudioFormat::ChannelConfigSurround5Dot0 },
191 { kAudioChannelLayoutTag_MPEG_5_1_A, QAudioFormat::ChannelConfigSurround5Dot1 },
192 { kAudioChannelLayoutTag_MPEG_6_1_A, QAudioFormat::channelConfig(QAudioFormat::FrontLeft,
193 QAudioFormat::FrontRight,
194 QAudioFormat::FrontCenter,
195 QAudioFormat::LFE,
196 QAudioFormat::BackLeft,
197 QAudioFormat::BackRight,
198 QAudioFormat::BackCenter) },
199 { kAudioChannelLayoutTag_MPEG_7_1_A, QAudioFormat::ChannelConfigSurround7Dot1 },
200 { kAudioChannelLayoutTag_SMPTE_DTV, QAudioFormat::channelConfig(QAudioFormat::FrontLeft,
201 QAudioFormat::FrontRight,
202 QAudioFormat::FrontCenter,
203 QAudioFormat::LFE,
204 QAudioFormat::BackLeft,
205 QAudioFormat::BackRight,
206 QAudioFormat::TopFrontLeft,
207 QAudioFormat::TopFrontRight) },
208
209 { kAudioChannelLayoutTag_ITU_2_1, QAudioFormat::ChannelConfig2Dot1 },
210 { kAudioChannelLayoutTag_ITU_2_2, QAudioFormat::channelConfig(QAudioFormat::FrontLeft,
211 QAudioFormat::FrontRight,
212 QAudioFormat::BackLeft,
213 QAudioFormat::BackRight) }
215
216
217QAudioFormat::ChannelConfig CoreAudioUtils::fromAudioChannelLayout(const AudioChannelLayout *layout)
218{
219 for (const auto &m : layoutTagMap) {
220 if (m.tag == layout->mChannelLayoutTag)
221 return m.channelConfig;
222 }
223
224 quint32 channels = 0;
225 if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
226 // special case 1 and 2 channel configs, as they are often reported without proper descriptions
227 if (layout->mNumberChannelDescriptions == 1
228 && (layout->mChannelDescriptions[0].mChannelLabel == kAudioChannelLabel_Unknown
229 || layout->mChannelDescriptions[0].mChannelLabel == kAudioChannelLabel_Mono))
230 return QAudioFormat::ChannelConfigMono;
231 if (layout->mNumberChannelDescriptions == 2 &&
232 layout->mChannelDescriptions[0].mChannelLabel == kAudioChannelLabel_Unknown &&
233 layout->mChannelDescriptions[1].mChannelLabel == kAudioChannelLabel_Unknown)
234 return QAudioFormat::ChannelConfigStereo;
235
236 for (uint i = 0; i < layout->mNumberChannelDescriptions; ++i) {
237 const auto channelLabel = layout->mChannelDescriptions[i].mChannelLabel;
238 if (channelLabel == kAudioChannelLabel_Unknown) {
239 // Any number of unknown channel labels occurs for loopback audio devices.
240 // E.g. the case is reproduced with installed software Soundflower.
241 continue;
242 }
243
244 const auto found = std::find_if(channelMap, std::end(channelMap),
245 [channelLabel](const auto &labelWithPos) {
246 return labelWithPos.label == channelLabel;
247 });
248
249 if (found == std::end(channelMap))
250 qWarning() << "audio device has unrecognized channel, index:" << i
251 << "label:" << channelLabel;
252 else
253 channels |= QAudioFormat::channelConfig(found->pos);
254 }
255 } else {
256 qWarning() << "Channel layout uses unimplemented format, channelLayoutTag:"
257 << layout->mChannelLayoutTag;
258 }
259 return QAudioFormat::ChannelConfig(channels);
260}
261
262QT_END_NAMESPACE
static double frequency()
Combined button and popup list for selecting options.
AudioChannelLayoutTag tag
QAudioFormat::ChannelConfig channelConfig
AudioChannelLabel label
QAudioFormat::AudioChannelPosition pos