diff options
author | Lars Knoll <lars.knoll@qt.io> | 2022-04-19 16:04:44 +0200 |
---|---|---|
committer | Lars Knoll <lars.knoll@qt.io> | 2022-05-05 10:07:29 +0200 |
commit | 2a0cdd8aa2b1c738f3c842e99afa12cabd0077c5 (patch) | |
tree | cb6c0f72fc80a1801c9fd1d1b64771582b3a830d | |
parent | 9f9f1726a654906250593c67c1553a94d25cf101 (diff) |
Retrieve channel configuration on macOS
iOS needs some more work.
Change-Id: I11048cce5d3da8a7b6e2477a2453cfac43f75231
Reviewed-by: Rafael Roquetto <rafael.roquetto@qt.io>
-rw-r--r-- | src/multimedia/audio/qaudioformat.h | 1 | ||||
-rw-r--r-- | src/multimedia/darwin/qcoreaudioutils.mm | 151 | ||||
-rw-r--r-- | src/multimedia/darwin/qcoreaudioutils_p.h | 4 | ||||
-rw-r--r-- | src/multimedia/darwin/qdarwinaudiodevice.mm | 50 | ||||
-rw-r--r-- | src/multimedia/darwin/qdarwinaudiodevice_p.h | 1 | ||||
-rw-r--r-- | src/multimedia/darwin/qdarwinaudiosink.mm | 4 |
6 files changed, 196 insertions, 15 deletions
diff --git a/src/multimedia/audio/qaudioformat.h b/src/multimedia/audio/qaudioformat.h index 03e312247..b4cbb6428 100644 --- a/src/multimedia/audio/qaudioformat.h +++ b/src/multimedia/audio/qaudioformat.h @@ -97,6 +97,7 @@ public: BottomFrontLeft, BottomFrontRight }; + static constexpr int NChannelPositions = BottomFrontRight + 1; enum ChannelConfig : quint32 { ChannelConfigUnknown = 0, diff --git a/src/multimedia/darwin/qcoreaudioutils.mm b/src/multimedia/darwin/qcoreaudioutils.mm index 8faa353c7..c8ee541cf 100644 --- a/src/multimedia/darwin/qcoreaudioutils.mm +++ b/src/multimedia/darwin/qcoreaudioutils.mm @@ -38,6 +38,7 @@ ****************************************************************************/ #include "qcoreaudioutils_p.h" +#include <qdebug.h> #include <mach/mach_time.h> QT_BEGIN_NAMESPACE @@ -134,6 +135,156 @@ AudioStreamBasicDescription CoreAudioUtils::toAudioStreamBasicDescription(QAudio return sf; } + +static constexpr struct { + QAudioFormat::AudioChannelPosition pos; + AudioChannelLabel label; +} channelMap[] = { + { QAudioFormat::FrontLeft, kAudioChannelLabel_Left }, + { QAudioFormat::FrontRight, kAudioChannelLabel_Right }, + { QAudioFormat::FrontCenter, kAudioChannelLabel_Center }, + { QAudioFormat::LFE, kAudioChannelLabel_LFEScreen }, + { QAudioFormat::BackLeft, kAudioChannelLabel_LeftSurround }, + { QAudioFormat::BackRight, kAudioChannelLabel_RightSurround }, + { QAudioFormat::FrontLeftOfCenter, kAudioChannelLabel_LeftCenter }, + { QAudioFormat::FrontRightOfCenter, kAudioChannelLabel_RightCenter }, + { QAudioFormat::BackCenter, kAudioChannelLabel_CenterSurround }, + { QAudioFormat::LFE2, kAudioChannelLabel_LFE2 }, + { QAudioFormat::SideLeft, kAudioChannelLabel_LeftSurroundDirect }, // ??? + { QAudioFormat::SideRight, kAudioChannelLabel_RightSurroundDirect }, // ??? + { QAudioFormat::TopFrontLeft, kAudioChannelLabel_VerticalHeightLeft }, + { QAudioFormat::TopFrontRight, kAudioChannelLabel_VerticalHeightRight }, + { QAudioFormat::TopFrontCenter, kAudioChannelLabel_VerticalHeightCenter }, + { QAudioFormat::TopCenter, kAudioChannelLabel_CenterTopMiddle }, + { QAudioFormat::TopBackLeft, kAudioChannelLabel_TopBackLeft }, + { QAudioFormat::TopBackRight, kAudioChannelLabel_TopBackRight }, + { QAudioFormat::TopSideLeft, kAudioChannelLabel_LeftTopMiddle }, + { QAudioFormat::TopSideRight, kAudioChannelLabel_RightTopMiddle }, + { QAudioFormat::TopBackCenter, kAudioChannelLabel_TopBackCenter }, +}; + +std::unique_ptr<AudioChannelLayout> CoreAudioUtils::toAudioChannelLayout(const QAudioFormat &format, UInt32 *size) +{ + auto channelConfig = format.channelConfig(); + if (channelConfig == QAudioFormat::ChannelConfigUnknown) + channelConfig = QAudioFormat::defaultChannelConfigForChannelCount(format.channelCount()); + + *size = sizeof(AudioChannelLayout) + int(QAudioFormat::NChannelPositions)*sizeof(AudioChannelDescription); + auto *layout = static_cast<AudioChannelLayout *>(malloc(*size)); + memset(layout, 0, *size); + layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions; + + for (const auto &m : channelMap) { + if (channelConfig & QAudioFormat::channelConfig(m.pos)) + layout->mChannelDescriptions[layout->mNumberChannelDescriptions++].mChannelLabel = m.label; + } + + if (channelConfig & QAudioFormat::channelConfig(QAudioFormat::BottomFrontCenter)) { + auto &desc = layout->mChannelDescriptions[layout->mNumberChannelDescriptions++]; + desc.mChannelLabel = kAudioChannelLabel_UseCoordinates; + desc.mChannelFlags = kAudioChannelFlags_SphericalCoordinates; + desc.mCoordinates[kAudioChannelCoordinates_Azimuth] = 0.f; + desc.mCoordinates[kAudioChannelCoordinates_Elevation] = -20.; + desc.mCoordinates[kAudioChannelCoordinates_Distance] = 1.f; + } + if (channelConfig & QAudioFormat::channelConfig(QAudioFormat::BottomFrontLeft)) { + auto &desc = layout->mChannelDescriptions[layout->mNumberChannelDescriptions++]; + desc.mChannelLabel = kAudioChannelLabel_UseCoordinates; + desc.mChannelFlags = kAudioChannelFlags_SphericalCoordinates; + desc.mCoordinates[kAudioChannelCoordinates_Azimuth] = -45.f; + desc.mCoordinates[kAudioChannelCoordinates_Elevation] = -20.; + desc.mCoordinates[kAudioChannelCoordinates_Distance] = 1.f; + } + if (channelConfig & QAudioFormat::channelConfig(QAudioFormat::BottomFrontRight)) { + auto &desc = layout->mChannelDescriptions[layout->mNumberChannelDescriptions++]; + desc.mChannelLabel = kAudioChannelLabel_UseCoordinates; + desc.mChannelFlags = kAudioChannelFlags_SphericalCoordinates; + desc.mCoordinates[kAudioChannelCoordinates_Azimuth] = 45.f; + desc.mCoordinates[kAudioChannelCoordinates_Elevation] = -20.; + desc.mCoordinates[kAudioChannelCoordinates_Distance] = 1.f; + } + + return std::unique_ptr<AudioChannelLayout>(layout); +} + +static constexpr struct { + AudioChannelLayoutTag tag; + QAudioFormat::ChannelConfig channelConfig; +} layoutTagMap[] = { + { kAudioChannelLayoutTag_Mono, QAudioFormat::ChannelConfigMono }, + { kAudioChannelLayoutTag_Stereo, QAudioFormat::ChannelConfigStereo }, + { kAudioChannelLayoutTag_StereoHeadphones, QAudioFormat::ChannelConfigStereo }, + { kAudioChannelLayoutTag_MPEG_1_0, QAudioFormat::ChannelConfigMono }, + { kAudioChannelLayoutTag_MPEG_2_0, QAudioFormat::ChannelConfigStereo }, + { kAudioChannelLayoutTag_MPEG_3_0_A, QAudioFormat::channelConfig(QAudioFormat::FrontLeft, + QAudioFormat::FrontRight, + QAudioFormat::FrontCenter) }, + { kAudioChannelLayoutTag_MPEG_4_0_A, QAudioFormat::channelConfig(QAudioFormat::FrontLeft, + QAudioFormat::FrontRight, + QAudioFormat::FrontCenter, + QAudioFormat::BackCenter) }, + { kAudioChannelLayoutTag_MPEG_5_0_A, QAudioFormat::ChannelConfigSurround5Dot0 }, + { kAudioChannelLayoutTag_MPEG_5_1_A, QAudioFormat::ChannelConfigSurround5Dot1 }, + { kAudioChannelLayoutTag_MPEG_6_1_A, QAudioFormat::channelConfig(QAudioFormat::FrontLeft, + QAudioFormat::FrontRight, + QAudioFormat::FrontCenter, + QAudioFormat::LFE, + QAudioFormat::BackLeft, + QAudioFormat::BackRight, + QAudioFormat::BackCenter) }, + { kAudioChannelLayoutTag_MPEG_7_1_A, QAudioFormat::ChannelConfigSurround7Dot1 }, + { kAudioChannelLayoutTag_SMPTE_DTV, QAudioFormat::channelConfig(QAudioFormat::FrontLeft, + QAudioFormat::FrontRight, + QAudioFormat::FrontCenter, + QAudioFormat::LFE, + QAudioFormat::BackLeft, + QAudioFormat::BackRight, + QAudioFormat::TopFrontLeft, + QAudioFormat::TopFrontRight) }, + + { kAudioChannelLayoutTag_ITU_2_1, QAudioFormat::ChannelConfig2Dot1 }, + { kAudioChannelLayoutTag_ITU_2_2, QAudioFormat::channelConfig(QAudioFormat::FrontLeft, + QAudioFormat::FrontRight, + QAudioFormat::BackLeft, + QAudioFormat::BackRight) } +}; + + +QAudioFormat::ChannelConfig CoreAudioUtils::fromAudioChannelLayout(const AudioChannelLayout *layout) +{ + for (const auto &m : layoutTagMap) { + if (m.tag == layout->mChannelLayoutTag) + return m.channelConfig; + } + + quint32 channels = 0; + if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) { + // special case 1 and 2 channel configs, as they are often reported without proper descriptions + if (layout->mNumberChannelDescriptions == 1 && layout->mChannelDescriptions[0].mChannelLabel == kAudioChannelLabel_Unknown) + return QAudioFormat::ChannelConfigMono; + if (layout->mNumberChannelDescriptions == 2 && + layout->mChannelDescriptions[0].mChannelLabel == kAudioChannelLabel_Unknown && + layout->mChannelDescriptions[1].mChannelLabel == kAudioChannelLabel_Unknown) + return QAudioFormat::ChannelConfigStereo; + + for (uint i = 0; i < layout->mNumberChannelDescriptions; ++i) { + bool found = false; + for (const auto &m : channelMap) { + if (layout->mChannelDescriptions[i].mChannelLabel == m.label) { + channels |= QAudioFormat::channelConfig(m.pos); + found = true; + break; + } + } + if (!found) + qWarning() << "audio device has unknown channel" << layout->mChannelDescriptions[i].mChannelLabel; + } + } else { + qWarning() << "Channel layout uses unimplemented format"; + } + return QAudioFormat::ChannelConfig(channels); +} + // QAudioRingBuffer CoreAudioRingBuffer::CoreAudioRingBuffer(int bufferSize): m_bufferSize(bufferSize) diff --git a/src/multimedia/darwin/qcoreaudioutils_p.h b/src/multimedia/darwin/qcoreaudioutils_p.h index 9fce95b52..b6730c978 100644 --- a/src/multimedia/darwin/qcoreaudioutils_p.h +++ b/src/multimedia/darwin/qcoreaudioutils_p.h @@ -66,6 +66,10 @@ public: static Q_MULTIMEDIA_EXPORT QAudioFormat toQAudioFormat(const AudioStreamBasicDescription& streamFormat); static AudioStreamBasicDescription toAudioStreamBasicDescription(QAudioFormat const& audioFormat); + // ownership is transferred to caller, free with ::free() + static std::unique_ptr<AudioChannelLayout> toAudioChannelLayout(const QAudioFormat &format, UInt32 *size); + static QAudioFormat::ChannelConfig fromAudioChannelLayout(const AudioChannelLayout *layout); + private: static void initialize(); static double sFrequency; diff --git a/src/multimedia/darwin/qdarwinaudiodevice.mm b/src/multimedia/darwin/qdarwinaudiodevice.mm index 8fe6f1a2c..e23c087f8 100644 --- a/src/multimedia/darwin/qdarwinaudiodevice.mm +++ b/src/multimedia/darwin/qdarwinaudiodevice.mm @@ -53,22 +53,24 @@ QT_BEGIN_NAMESPACE #if defined(Q_OS_MACOS) - QCoreAudioDeviceInfo::QCoreAudioDeviceInfo(AudioDeviceID id, const QByteArray &device, QAudioDevice::Mode mode) - : QAudioDevicePrivate(device, mode), - m_deviceId(id) +QCoreAudioDeviceInfo::QCoreAudioDeviceInfo(AudioDeviceID id, const QByteArray &device, QAudioDevice::Mode mode) + : QAudioDevicePrivate(device, mode), + m_deviceId(id) #else - QCoreAudioDeviceInfo::QCoreAudioDeviceInfo(const QByteArray &device, QAudioDevice::Mode mode) - : QAudioDevicePrivate(device, mode) +QCoreAudioDeviceInfo::QCoreAudioDeviceInfo(const QByteArray &device, QAudioDevice::Mode mode) + : QAudioDevicePrivate(device, mode) #endif - { - preferredFormat = determinePreferredFormat(); - description = getDescription(); - minimumSampleRate = 1; - maximumSampleRate = 96000; - minimumChannelCount = 1; - maximumChannelCount = 16; - supportedSampleFormats << QAudioFormat::UInt8 << QAudioFormat::Int16 << QAudioFormat::Int32 << QAudioFormat::Float; - } +{ + description = getDescription(); + getChannelLayout(); + preferredFormat = determinePreferredFormat(); + minimumSampleRate = 1; + maximumSampleRate = 96000; + minimumChannelCount = 1; + maximumChannelCount = 16; + supportedSampleFormats << QAudioFormat::UInt8 << QAudioFormat::Int16 << QAudioFormat::Int32 << QAudioFormat::Float; + +} QAudioFormat QCoreAudioDeviceInfo::determinePreferredFormat() const @@ -121,6 +123,7 @@ QAudioFormat QCoreAudioDeviceInfo::determinePreferredFormat() const format.setSampleFormat(QAudioFormat::Int16); format.setChannelCount(mode == QAudioDevice::Input ? 1 : 2); } + format.setChannelConfig(channelConfiguration); return format; } @@ -150,4 +153,23 @@ QString QCoreAudioDeviceInfo::getDescription() const #endif } +void QCoreAudioDeviceInfo::getChannelLayout() +{ +#ifdef Q_OS_MACOS + AudioObjectPropertyAddress audioDeviceChannelLayoutPropertyAddress = { kAudioDevicePropertyPreferredChannelLayout, + (mode == QAudioDevice::Input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput), + kAudioObjectPropertyElementMaster }; + UInt32 propSize; + if (AudioObjectGetPropertyDataSize(m_deviceId, &audioDeviceChannelLayoutPropertyAddress, 0, nullptr, &propSize) == noErr) { + AudioChannelLayout *layout = static_cast<AudioChannelLayout *>(malloc(propSize)); + if (AudioObjectGetPropertyData(m_deviceId, &audioDeviceChannelLayoutPropertyAddress, 0, nullptr, &propSize, layout) == noErr) { + channelConfiguration = CoreAudioUtils::fromAudioChannelLayout(layout); + } + free(layout); + } +#else + channelConfiguration = (mode == QAudioDevice::Input) ? QAudioFormat::ChannelConfigMono : QAudioFormat::ChannelConfigStereo; +#endif +} + QT_END_NAMESPACE diff --git a/src/multimedia/darwin/qdarwinaudiodevice_p.h b/src/multimedia/darwin/qdarwinaudiodevice_p.h index 687e43f34..ab6758118 100644 --- a/src/multimedia/darwin/qdarwinaudiodevice_p.h +++ b/src/multimedia/darwin/qdarwinaudiodevice_p.h @@ -77,6 +77,7 @@ public: private: QAudioFormat determinePreferredFormat() const; QString getDescription() const; + void getChannelLayout(); #if defined(Q_OS_MACOS) AudioDeviceID m_deviceId; #endif diff --git a/src/multimedia/darwin/qdarwinaudiosink.mm b/src/multimedia/darwin/qdarwinaudiosink.mm index c4ade78f4..49443d234 100644 --- a/src/multimedia/darwin/qdarwinaudiosink.mm +++ b/src/multimedia/darwin/qdarwinaudiosink.mm @@ -555,11 +555,13 @@ bool QDarwinAudioSink::open() return false; } #endif + UInt32 size; + // Set stream format m_streamFormat = CoreAudioUtils::toAudioStreamBasicDescription(m_audioFormat); + size = sizeof(m_streamFormat); - UInt32 size = sizeof(m_streamFormat); if (AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, |