// Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QMACOSAUDIODATAUTILS_P_H #define QMACOSAUDIODATAUTILS_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE namespace QCoreAudioUtils { // coreaudio string helpers QStringView audioPropertySelectorToString(AudioObjectPropertySelector); QStringView audioPropertyScopeToString(AudioObjectPropertyScope); QStringView audioPropertyElementToString(AudioObjectPropertyElement); [[nodiscard]] QByteArray readPersistentDeviceId( AudioDeviceID, QAudioDevice::Mode); [[nodiscard]] std::optional findAudioDeviceId( const QByteArray &id, QAudioDevice::Mode); [[nodiscard]] std::optional findAudioDeviceId(const QAudioDevice &device); template void printUnableToReadWarning(AudioObjectID objectID, const AudioObjectPropertyAddress &address, Args &&...args) { using namespace QCoreAudioUtils; auto warn = qWarning(); warn << "Unable to read property" << QCoreAudioUtils::audioPropertySelectorToString(address.mSelector) << "for object" << objectID << ", scope" << QCoreAudioUtils::audioPropertyScopeToString(address.mScope) << ";"; (warn << ... << args); warn << "\n If the warning is unexpected use test_audio_config to get comprehensive audio info and report a bug"; } [[nodiscard]] AudioObjectPropertyAddress makePropertyAddress( AudioObjectPropertySelector selector, QAudioDevice::Mode mode, AudioObjectPropertyElement element = kAudioObjectPropertyElementMain); [[nodiscard]] bool getAudioPropertyRaw( AudioObjectID objectID, const AudioObjectPropertyAddress &address, QSpan destination, bool warnIfMissing = true); template std::optional> getAudioPropertyList(AudioObjectID objectID, const AudioObjectPropertyAddress &address, bool warnIfMissing = true) { static_assert(std::is_trivial_v, "A trivial type is expected"); UInt32 size = 0; const auto res = AudioObjectGetPropertyDataSize(objectID, &address, 0, nullptr, &size); if (res != noErr) { if (warnIfMissing) printUnableToReadWarning(objectID, address, "AudioObjectGetPropertyDataSize failed, Err:", res); } else { std::vector data(size / sizeof(T)); if (getAudioPropertyRaw(objectID, address, as_writable_bytes(QSpan{data}))) return data; } return {}; } template std::optional getAudioProperty(AudioObjectID objectID, const AudioObjectPropertyAddress &address, bool warnIfMissing = false) { if constexpr(std::is_same_v) { const std::optional string = getAudioProperty( objectID, address, warnIfMissing); if (string) return QCFString{*string}; return {}; } else { static_assert(std::is_trivial_v, "A trivial type is expected"); T object{}; if (getAudioPropertyRaw(objectID, address, as_writable_bytes(QSpan(&object, 1)), warnIfMissing)) return object; return {}; } } template std::unique_ptr getAudioPropertyWithFlexibleArrayMember(AudioObjectID objectID, const AudioObjectPropertyAddress &address, bool warnIfMissing = false) { static_assert(std::is_trivial_v, "A trivial type is expected"); UInt32 size = 0; const auto res = AudioObjectGetPropertyDataSize(objectID, &address, 0, nullptr, &size); if (res != noErr) { if (warnIfMissing) printUnableToReadWarning(objectID, address, "AudioObjectGetPropertyDataSize failed, Err:", res); return nullptr; } if (size < sizeof(T)) { printUnableToReadWarning(objectID, address, "Data size is too small:", size, "VS", sizeof(T), "bytes"); return nullptr; } using QCoreAudioUtils::QFreeDeleter; std::unique_ptr region { reinterpret_cast(::malloc(size)) }; if (getAudioPropertyRaw(objectID, address, QSpan(region.get(), size), warnIfMissing)) return std::unique_ptr{ reinterpret_cast(region.release()) }; return {}; } } // namespace QCoreAudioUtils QT_END_NAMESPACE #endif // QMACOSAUDIODATAUTILS_P_H