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
qmacosaudiodatautils.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 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
5
7
8namespace QCoreAudioUtils
9{
10
11#define ENUM_CASE(NAME) case
12 NAME: return QStringView(QT_UNICODE_LITERAL(QT_STRINGIFY(NAME)))
13
14#define DUPLICATE_ENUM_CASE(NAME) static_assert(true, "force semicolon")
15
16QStringView audioPropertySelectorToString(AudioObjectPropertySelector selector)
17{
18 switch (selector) {
19 // AudioObject properties
20 ENUM_CASE(kAudioObjectPropertyBaseClass );
21 ENUM_CASE(kAudioObjectPropertyClass );
22 ENUM_CASE(kAudioObjectPropertyOwner );
23 ENUM_CASE(kAudioObjectPropertyName );
24 ENUM_CASE(kAudioObjectPropertyModelName );
25 ENUM_CASE(kAudioObjectPropertyManufacturer );
26 ENUM_CASE(kAudioObjectPropertyElementName );
27 ENUM_CASE(kAudioObjectPropertyElementCategoryName);
28 ENUM_CASE(kAudioObjectPropertyElementNumberName );
29 ENUM_CASE(kAudioObjectPropertyOwnedObjects );
30 ENUM_CASE(kAudioObjectPropertyIdentify );
31 ENUM_CASE(kAudioObjectPropertySerialNumber );
32 ENUM_CASE(kAudioObjectPropertyFirmwareVersion );
33 ENUM_CASE(kAudioObjectPropertySelectorWildcard );
34
35 // AudioDevice properties
36 ENUM_CASE(kAudioDevicePropertyConfigurationApplication );
37 ENUM_CASE(kAudioDevicePropertyDeviceUID );
38 ENUM_CASE(kAudioDevicePropertyModelUID );
39 ENUM_CASE(kAudioDevicePropertyTransportType );
40 ENUM_CASE(kAudioDevicePropertyRelatedDevices );
41 ENUM_CASE(kAudioDevicePropertyClockDomain );
42 ENUM_CASE(kAudioDevicePropertyDeviceIsAlive );
43 ENUM_CASE(kAudioDevicePropertyDeviceIsRunning );
44 ENUM_CASE(kAudioDevicePropertyDeviceCanBeDefaultDevice );
45 ENUM_CASE(kAudioDevicePropertyDeviceCanBeDefaultSystemDevice);
46 ENUM_CASE(kAudioDevicePropertyLatency );
47 ENUM_CASE(kAudioDevicePropertyStreams );
48 ENUM_CASE(kAudioObjectPropertyControlList );
49 ENUM_CASE(kAudioDevicePropertySafetyOffset );
50 ENUM_CASE(kAudioDevicePropertyNominalSampleRate );
51 ENUM_CASE(kAudioDevicePropertyAvailableNominalSampleRates );
52 ENUM_CASE(kAudioDevicePropertyIcon );
53 ENUM_CASE(kAudioDevicePropertyIsHidden );
54 ENUM_CASE(kAudioDevicePropertyPreferredChannelsForStereo );
55 ENUM_CASE(kAudioDevicePropertyPreferredChannelLayout );
56
57 // AudioClockDevice properties
58 ENUM_CASE(kAudioClockDevicePropertyDeviceUID );
59 DUPLICATE_ENUM_CASE(kAudioClockDevicePropertyTransportType );
60 DUPLICATE_ENUM_CASE(kAudioClockDevicePropertyClockDomain );
61 DUPLICATE_ENUM_CASE(kAudioClockDevicePropertyDeviceIsAlive );
62 DUPLICATE_ENUM_CASE(kAudioClockDevicePropertyDeviceIsRunning );
63 DUPLICATE_ENUM_CASE(kAudioClockDevicePropertyLatency );
64 DUPLICATE_ENUM_CASE(kAudioClockDevicePropertyControlList );
65 DUPLICATE_ENUM_CASE(kAudioClockDevicePropertyNominalSampleRate );
66 DUPLICATE_ENUM_CASE(kAudioClockDevicePropertyAvailableNominalSampleRates);
67
68 // AudioEndPointDevice properties
69 ENUM_CASE(kAudioEndPointDevicePropertyComposition );
70 ENUM_CASE(kAudioEndPointDevicePropertyEndPointList);
71 ENUM_CASE(kAudioEndPointDevicePropertyIsPrivate );
72
73 // AudioStream properties
74 ENUM_CASE(kAudioStreamPropertyIsActive );
75 ENUM_CASE(kAudioStreamPropertyDirection );
76 ENUM_CASE(kAudioStreamPropertyTerminalType );
77 ENUM_CASE(kAudioStreamPropertyStartingChannel );
78 ENUM_CASE(kAudioStreamPropertyVirtualFormat );
79 ENUM_CASE(kAudioStreamPropertyAvailableVirtualFormats );
80 ENUM_CASE(kAudioStreamPropertyPhysicalFormat );
81 ENUM_CASE(kAudioStreamPropertyAvailablePhysicalFormats);
82
83 default:
84 Q_UNREACHABLE_RETURN(u"");
85 }
86}
87
88QStringView audioPropertyScopeToString(AudioObjectPropertyScope scope)
89{
90 switch (scope) {
91 ENUM_CASE(kAudioObjectPropertyScopeGlobal );
92 ENUM_CASE(kAudioObjectPropertyScopeInput );
93 ENUM_CASE(kAudioObjectPropertyScopeOutput );
94 ENUM_CASE(kAudioObjectPropertyScopePlayThrough);
95 ENUM_CASE(kAudioObjectPropertyScopeWildcard );
96 default:
97 Q_UNREACHABLE_RETURN(u"");
98 }
99}
100
101QStringView audioPropertyElementToString(AudioObjectPropertyElement element)
102{
103 switch (element) {
104 ENUM_CASE(kAudioObjectPropertyElementMain );
105 ENUM_CASE(kAudioObjectPropertyElementWildcard);
106 default:
107 Q_UNREACHABLE_RETURN(u"");
108 }
109}
110
111#undef ENUM_CASE
112#undef DUPLICATE_ENUM_CASE
113
114}
115
118 AudioObjectPropertySelector selector,
119 QAudioDevice::Mode mode,
120 AudioObjectPropertyElement element)
121{
122 return { selector,
123 mode == QAudioDevice::Input ? kAudioDevicePropertyScopeInput
124 : kAudioDevicePropertyScopeOutput,
125 element };
126}
127
128bool QCoreAudioUtils::getAudioPropertyRaw(AudioObjectID objectID, const AudioObjectPropertyAddress &address,
129 QSpan<std::byte> destination, bool warnIfMissing)
130{
131 UInt32 readBytes = destination.size();
132 const auto res =
133 AudioObjectGetPropertyData(objectID, &address, 0, nullptr, &readBytes, destination.data());
134
135 if (res != noErr) {
136 if (warnIfMissing)
137 printUnableToReadWarning(objectID, address, "Err:", res);
138 } else if (readBytes != destination.size()) {
139 if (warnIfMissing)
140 printUnableToReadWarning(objectID, address, "Data size", readBytes, "VS", destination.size(),
141 "expected");
142 } else {
143 return true;
144 }
145
146 return false;
147}
148
150 AudioDeviceID device,
151 QAudioDevice::Mode mode)
152{
153 const AudioObjectPropertyAddress propertyAddress = makePropertyAddress(
154 kAudioDevicePropertyDeviceUID,
155 mode);
156
157 const std::optional<QCFString> name = getAudioProperty<QCFString>(device, propertyAddress);
158 if (name)
159 return QString{*name}.toUtf8();
160
161 return QByteArray();
162}
163
165 const QByteArray &id,
166 QAudioDevice::Mode mode)
167{
168 if (id.isEmpty() || mode == QAudioDevice::Mode::Null)
169 return std::nullopt;
170 // Iterate over all the devices connected and find the one matching our ID and return the AudioDeviceID.
171 const AudioObjectPropertyAddress audioDevicesPropertyAddress = {
172 kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
173 kAudioObjectPropertyElementMain
174 };
175 const std::optional<std::vector<AudioDeviceID>> audioDevicesOpt = getAudioPropertyList<AudioDeviceID>(
176 kAudioObjectSystemObject,
177 audioDevicesPropertyAddress);
178 if (audioDevicesOpt.has_value()) {
179 const std::vector<AudioDeviceID> &audioDevices = audioDevicesOpt.value();
180 const AudioObjectPropertyAddress audioDeviceStreamFormatPropertyAddress = makePropertyAddress(
181 kAudioDevicePropertyStreamFormat,
182 mode);
183 for (const AudioDeviceID &device : audioDevices) {
184 // Ignore devices that don't have any audio formats we can use.
185 const std::optional<AudioStreamBasicDescription> audioStreamOpt =
186 getAudioProperty<AudioStreamBasicDescription>(
187 device,
188 audioDeviceStreamFormatPropertyAddress);
189 // Check that these devices have the same unique-id. In which case, we found the
190 // correct CoreAudioID.
191 if (audioStreamOpt.has_value() && readPersistentDeviceId(device, mode) == id)
192 return device;
193 }
194 }
195 return std::nullopt;
196}
197
199 const QAudioDevice &device)
200{
201 return findAudioDeviceId(device.id(), device.mode());
202}
203
204QT_END_NAMESPACE
std::optional< AudioDeviceID > findAudioDeviceId(const QAudioDevice &device)
QStringView audioPropertySelectorToString(AudioObjectPropertySelector selector)
QStringView audioPropertyElementToString(AudioObjectPropertyElement element)
AudioObjectPropertyAddress makePropertyAddress(AudioObjectPropertySelector selector, QAudioDevice::Mode mode, AudioObjectPropertyElement element=kAudioObjectPropertyElementMain)
QByteArray readPersistentDeviceId(AudioDeviceID, QAudioDevice::Mode)
bool getAudioPropertyRaw(AudioObjectID objectID, const AudioObjectPropertyAddress &address, QSpan< std::byte > destination, bool warnIfMissing=true)
std::optional< AudioDeviceID > findAudioDeviceId(const QByteArray &id, QAudioDevice::Mode)
QStringView audioPropertyScopeToString(AudioObjectPropertyScope scope)
#define ENUM_CASE(NAME)
#define DUPLICATE_ENUM_CASE(NAME)