summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2022-04-19 16:04:44 +0200
committerLars Knoll <lars.knoll@qt.io>2022-05-05 10:07:29 +0200
commit2a0cdd8aa2b1c738f3c842e99afa12cabd0077c5 (patch)
treecb6c0f72fc80a1801c9fd1d1b64771582b3a830d
parent9f9f1726a654906250593c67c1553a94d25cf101 (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.h1
-rw-r--r--src/multimedia/darwin/qcoreaudioutils.mm151
-rw-r--r--src/multimedia/darwin/qcoreaudioutils_p.h4
-rw-r--r--src/multimedia/darwin/qdarwinaudiodevice.mm50
-rw-r--r--src/multimedia/darwin/qdarwinaudiodevice_p.h1
-rw-r--r--src/multimedia/darwin/qdarwinaudiosink.mm4
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,