Revision control
++ // Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++
++ #include "qcoreaudiodevice_p.h"
++
++ #include <QtCore/private/qcore_mac_p.h>
++
++ #include <QtMultimedia/private/qcoreaudioutils_p.h>
++ #include <QtMultimedia/private/qaudioformat_p.h>
++ #ifdef Q_OS_MACOS
++ #include <QtMultimedia/private/qmacosaudiodatautils_p.h>
++ #endif
++
++ #include <optional>
++
++ QT_BEGIN_NAMESPACE
++
++ namespace {
++
++ [[nodiscard]] QAudioFormat qDefaultPreferredFormat(
++ QAudioDevice::Mode mode,
++ QAudioFormat::ChannelConfig channelConfig)
++ {
++ QAudioFormat format;
++ format.setSampleRate(44100);
++ format.setSampleFormat(QAudioFormat::Int16);
++ format.setChannelCount(mode == QAudioDevice::Input ? 1 : 2);
++ format.setChannelConfig(channelConfig);
++ return format;
++ }
++
++ [[nodiscard]] QAudioFormat::ChannelConfig qGetDefaultChannelLayout(QAudioDevice::Mode mode)
++ {
++ return (mode == QAudioDevice::Input) ? QAudioFormat::ChannelConfigMono : QAudioFormat::ChannelConfigStereo;
++ }
++
++ [[nodiscard]] QString qGetDefaultDescription(const QByteArray &id)
++ {
++ return QString::fromUtf8(id);
++ }
++
++ #ifdef Q_OS_MACOS
++
++ [[nodiscard]] std::optional<QAudioFormat> qGetPreferredFormatForCoreAudioDevice(
++ QAudioDevice::Mode mode,
++ AudioDeviceID deviceId)
++ {
++ using namespace QCoreAudioUtils;
++
++ const auto audioDevicePropertyStreamsAddress =
++ makePropertyAddress(kAudioDevicePropertyStreams, mode);
++ const auto audioDevicePhysicalFormatPropertyAddress =
++ makePropertyAddress(kAudioStreamPropertyPhysicalFormat, mode);
++
++ auto streamIDs =
++ getAudioPropertyList<AudioStreamID>(deviceId, audioDevicePropertyStreamsAddress);
++ if (!streamIDs || streamIDs->empty())
++ return std::nullopt;
++
++ for (auto streamID : *streamIDs) {
++ auto streamDescription = getAudioProperty<AudioStreamBasicDescription>(
++ streamID, audioDevicePhysicalFormatPropertyAddress);
++ if (!streamDescription)
++ continue;
++
++ QAudioFormat fmt = QCoreAudioUtils::toPreferredQAudioFormat(*streamDescription);
++ if (fmt.isValid())
++ return fmt;
++ }
++ return std::nullopt;
++ }
++
++ [[nodiscard]] std::optional<QAudioFormat::ChannelConfig> qGetChannelLayoutForCoreAudioDevice(
++ QAudioDevice::Mode mode,
++ AudioDeviceID deviceId)
++ {
++ using namespace QCoreAudioUtils;
++
++ const auto propertyAddress =
++ makePropertyAddress(kAudioDevicePropertyPreferredChannelLayout, mode);
++
++ if (auto layout = getAudioPropertyWithFlexibleArrayMember<AudioChannelLayout>(deviceId, propertyAddress))
++ return QCoreAudioUtils::fromAudioChannelLayout(layout.get());
++
++ return std::nullopt;
++ }
++
++ [[nodiscard]] std::optional<QString> qGetDescriptionForCoreAudioDevice(
++ QAudioDevice::Mode mode,
++ AudioDeviceID deviceId)
++ {
++ using namespace QCoreAudioUtils;
++
++ const auto propertyAddress = makePropertyAddress(kAudioObjectPropertyName, mode);
++ if (auto name = getAudioProperty<QCFString>(deviceId, propertyAddress))
++ return name;
++
++ return std::nullopt;
++ }
++
++ [[nodiscard]] std::optional<int> qSupportedNumberOfChannels(
++ QAudioDevice::Mode mode,
++ AudioDeviceID deviceId)
++ {
++ using namespace QCoreAudioUtils;
++
++ const auto audioDevicePropertyStreamsAddress =
++ makePropertyAddress(kAudioDevicePropertyStreams, mode);
++
++ auto streamIDs = getAudioPropertyList<AudioStreamID>(deviceId, audioDevicePropertyStreamsAddress);
++ if (!streamIDs)
++ return std::nullopt;
++
++ const auto propVirtualFormat = makePropertyAddress(kAudioStreamPropertyVirtualFormat, mode);
++
++ int ret{};
++
++ for (auto streamID : *streamIDs) {
++ auto streamDescription = getAudioProperty<AudioStreamBasicDescription>(streamID, propVirtualFormat);
++ if (!streamDescription)
++ continue;
++ ret += streamDescription->mChannelsPerFrame;
++ }
++
++ return ret;
++ }
++
++ QAudioDevicePrivate::AudioDeviceFormat probeCoreAudioDeviceFormat(AudioDeviceID id,
++ QAudioDevice::Mode mode)
++ {
++ QAudioDevicePrivate::AudioDeviceFormat format;
++
++ const std::optional<QAudioFormat::ChannelConfig> channelConfigOpt =
++ qGetChannelLayoutForCoreAudioDevice(mode, id);
++ if (channelConfigOpt.has_value())
++ format.channelConfiguration = channelConfigOpt.value();
++ else
++ format.channelConfiguration = qGetDefaultChannelLayout(mode);
++
++ const std::optional<QAudioFormat> preferredFormatOpt =
++ qGetPreferredFormatForCoreAudioDevice(mode, id);
++ if (preferredFormatOpt.has_value())
++ format.preferredFormat = preferredFormatOpt.value();
++ else
++ format.preferredFormat = qDefaultPreferredFormat(mode, format.channelConfiguration);
++
++ format.minimumSampleRate = QtMultimediaPrivate::allSupportedSampleRates.front();
++ format.maximumSampleRate = QtMultimediaPrivate::allSupportedSampleRates.back();
++ format.minimumChannelCount = 1;
++ format.maximumChannelCount = qSupportedNumberOfChannels(mode, id).value_or(16);
++
++ format.supportedSampleFormats = qAllSupportedSampleFormats();
++
++ return format;
++ }
++
++ #else
++
++ QAudioDevicePrivate::AudioDeviceFormat createDefaultCoreAudioDeviceFormat(QAudioDevice::Mode mode)
++ {
++ QAudioDevicePrivate::AudioDeviceFormat format;
++
++ format.channelConfiguration = qGetDefaultChannelLayout(mode);
++ format.preferredFormat = qDefaultPreferredFormat(mode, format.channelConfiguration);
++
++ format.minimumSampleRate = 1;
++ format.maximumSampleRate = 96000;
++ format.minimumChannelCount = 1;
++ format.maximumChannelCount = 16;
++ format.supportedSampleFormats = qAllSupportedSampleFormats();
++
++ return format;
++ }
++
++ #endif
++
++ } // namespace
++
++ #ifdef Q_OS_MACOS
++
++ static QString getDescription(AudioDeviceID id, const QByteArray &device, QAudioDevice::Mode mode)
++ {
++ if (auto optionalDescription = qGetDescriptionForCoreAudioDevice(mode, id))
++ return *optionalDescription;
++ return qGetDefaultDescription(device);
++ }
++
++ QCoreAudioDeviceInfo::QCoreAudioDeviceInfo(AudioDeviceID id, const QByteArray &device,
++ QAudioDevice::Mode mode)
++ : QAudioDevicePrivate(device, mode, getDescription(id, device, mode), false,
++ probeCoreAudioDeviceFormat(id, mode))
++ {
++ }
++
++ #else
++
++ QCoreAudioDeviceInfo::QCoreAudioDeviceInfo(const QByteArray &device, QAudioDevice::Mode mode)
++ : QAudioDevicePrivate(device, mode, qGetDefaultDescription(device), false,
++ createDefaultCoreAudioDeviceFormat(mode))
++ {
++ }
++
++ #endif
++
++ QT_END_NAMESPACE