58 if (AudioObjectGetPropertyData(deviceId, &uidAddress, 0,
nullptr, &size, &uid) != noErr || !uid)
103std::unique_ptr<QCoreAudioAggregateDevice> QCoreAudioAggregateDevice::create(Direction direction)
105 const std::optional<QString> subDeviceUid = defaultDeviceUid(direction);
111 constexpr int maxAttempts = 3;
112 for (
int attempt = 0; attempt != maxAttempts; ++attempt) {
113 const QString uniqueSuffix = QUuid::createUuid().toString(QUuid::Id128);
114 const QString name = u"QtTestAggregate"_s + uniqueSuffix;
115 const QString uid = u"io.qt.test.aggregate."_s + uniqueSuffix;
117 QCFType<CFMutableDictionaryRef> description{ CFDictionaryCreateMutable(
118 kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
119 &kCFTypeDictionaryValueCallBacks) };
120 QCFType<CFStringRef> cfName = name.toCFString();
121 QCFType<CFStringRef> cfUid = uid.toCFString();
122 QCFType<CFStringRef> cfSubDeviceUid = subDeviceUid->toCFString();
123 const int isPrivate = 1;
124 QCFType<CFNumberRef> cfIsPrivate =
125 CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &isPrivate);
127 QCFType<CFMutableDictionaryRef> subDevice{
128 CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
129 &kCFTypeDictionaryValueCallBacks),
131 CFDictionaryAddValue(subDevice, CFSTR(kAudioSubDeviceUIDKey), cfSubDeviceUid);
132 QCFType<CFMutableArrayRef> subDevices{
133 CFArrayCreateMutable(kCFAllocatorDefault, 1, &kCFTypeArrayCallBacks),
135 CFArrayAppendValue(subDevices, subDevice);
137 CFDictionaryAddValue(description, CFSTR(kAudioAggregateDeviceNameKey), cfName);
138 CFDictionaryAddValue(description, CFSTR(kAudioAggregateDeviceUIDKey), cfUid);
139 CFDictionaryAddValue(description, CFSTR(kAudioAggregateDeviceIsPrivateKey), cfIsPrivate);
140 CFDictionaryAddValue(description, CFSTR(kAudioAggregateDeviceSubDeviceListKey), subDevices);
142 AudioObjectID id = kAudioObjectUnknown;
143 if (AudioHardwareCreateAggregateDevice(description, &id) == noErr
144 && id != kAudioObjectUnknown) {
149 return std::unique_ptr<QCoreAudioAggregateDevice>{
150 new QCoreAudioAggregateDevice{ std::move(priv) },
159QCoreAudioAggregateDevice::waitForQtDevice(
std::chrono::milliseconds timeout)
const
161 namespace ranges = QtMultimediaPrivate::ranges;
163 QDeadlineTimer deadline{ timeout };
165 QCoreApplication::processEvents(QEventLoop::AllEvents, 5);
167 for (
auto devices : { QMediaDevices::audioOutputs(), QMediaDevices::audioInputs() }) {
168 const auto it = ranges::find_if(devices, [&](
const QAudioDevice &device) {
169 return device.description() == d->name;
171 if (it != devices.end())
175 std::this_thread::sleep_for(2ms);
176 }
while (!deadline.hasExpired());
181bool QCoreAudioAggregateDevice::destroyAndWaitForDisconnectNotification(
182 std::chrono::milliseconds timeout)
184 const AudioObjectID id = d->id;
185 if (id == kAudioObjectUnknown)
188 d->isAliveNotifications = 0;
190 if (AudioObjectAddPropertyListener(id, &isAliveAddress, onPropertyChanged,
191 &d->isAliveNotifications)
194 d->listenerInstalled =
true;
196 if (AudioHardwareDestroyAggregateDevice(id) != noErr) {
197 AudioObjectRemovePropertyListener(id, &isAliveAddress, onPropertyChanged,
198 &d->isAliveNotifications);
199 d->listenerInstalled =
false;
203 d->id = kAudioObjectUnknown;
204 d->listenerInstalled =
false;
207 QDeadlineTimer deadline{ timeout };
208 while (d->isAliveNotifications.load() == 0) {
209 if (deadline.hasExpired())
211 std::this_thread::sleep_for(1ms);
216 std::this_thread::sleep_for(20ms);