Source code
Revision control
Copy as Markdown
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "audiodevices.h"
#include <QMediaDevices>
#include <QMediaFormat>
#if QT_CONFIG(permissions)
#include <QPermission>
#endif
#include <bitset>
using namespace Qt::Literals;
// Utility functions for converting QAudioFormat fields into text
static QString toString(QAudioFormat::SampleFormat sampleFormat)
{
switch (sampleFormat) {
case QAudioFormat::UInt8:
return u"Unsigned 8 bit"_s;
case QAudioFormat::Int16:
return u"Signed 16 bit"_s;
case QAudioFormat::Int32:
return u"Signed 32 bit"_s;
case QAudioFormat::Float:
return u"Float"_s;
default:
return u"Unknown"_s;
}
}
static QString toString(QAudioFormat::ChannelConfig channelConfig)
{
switch (channelConfig) {
case QAudioFormat::ChannelConfigMono:
return u"Mono"_s;
case QAudioFormat::ChannelConfigStereo:
return u"Stereo"_s;
case QAudioFormat::ChannelConfig2Dot1:
return u"2.1"_s;
case QAudioFormat::ChannelConfig3Dot0:
return u"3.0"_s;
case QAudioFormat::ChannelConfigSurround5Dot0:
return u"5.0 Surround"_s;
case QAudioFormat::ChannelConfigSurround5Dot1:
return u"5.1 Surround"_s;
case QAudioFormat::ChannelConfigSurround7Dot0:
return u"7.0 Surround"_s;
case QAudioFormat::ChannelConfigSurround7Dot1:
return u"7.1 Surround"_s;
default:
break;
}
static auto channelLabels = std::array{
u"UnknownPosition",
u"FrontLeft",
u"FrontRight",
u"FrontCenter",
u"LFE",
u"BackLeft",
u"BackRight",
u"FrontLeftOfCenter",
u"FrontRightOfCenter",
u"BackCenter",
u"SideLeft",
u"SideRight",
u"TopCenter",
u"TopFrontLeft",
u"TopFrontCenter",
u"TopFrontRight",
u"TopBackLeft",
u"TopBackCenter",
u"TopBackRight",
u"LFE2",
u"TopSideLeft",
u"TopSideRight",
u"BottomFrontCenter",
u"BottomFrontLeft",
u"BottomFrontRight",
};
QStringList labels;
std::bitset<QAudioFormat::NChannelPositions> configBits(channelConfig);
for (size_t i = 0; i < configBits.size(); ++i) {
if (configBits.test(i))
labels.emplace_back(channelLabels[i]);
}
return labels.join(", ");
}
AudioDevicesBase::AudioDevicesBase(QWidget *parent) : QMainWindow(parent)
{
setupUi(this);
}
AudioDevicesBase::~AudioDevicesBase() = default;
AudioDevices::AudioDevices(QWidget *parent)
: AudioDevicesBase(parent), m_devices(new QMediaDevices(this))
{
init();
}
void AudioDevices::updateDevicePropertes()
{
QStringList sampleFormats;
for (auto format : m_deviceInfo.supportedSampleFormats())
sampleFormats << toString(format);
sampleFormatField->setText(sampleFormats.join(", "));
if (m_deviceInfo.minimumChannelCount() != m_deviceInfo.maximumChannelCount()) {
QString channelRange = u"%1 - %2"_s.arg(m_deviceInfo.minimumChannelCount())
.arg(m_deviceInfo.maximumChannelCount());
channelNumberField->setText(channelRange);
} else {
channelNumberField->setText(QString::number(m_deviceInfo.minimumChannelCount()));
}
QString sampleRateRange = u"%1 - %2 Hz"_s.arg(m_deviceInfo.minimumSampleRate())
.arg(m_deviceInfo.maximumSampleRate());
samplingRatesField->setText(sampleRateRange);
QString channelConfig = toString(m_deviceInfo.channelConfiguration());
channelLayoutField->setText(channelConfig);
QString preferredFormat = u"%1, %2 Hz, %3 channels (%4)"_s.arg(
toString(m_deviceInfo.preferredFormat().sampleFormat()),
QString::number(m_deviceInfo.preferredFormat().sampleRate()),
QString::number(m_deviceInfo.preferredFormat().channelCount()),
toString(m_deviceInfo.preferredFormat().channelConfig()));
preferredFormatField->setText(preferredFormat);
for (QLineEdit *field : { samplingRatesField, channelNumberField, sampleFormatField,
channelLayoutField, preferredFormatField })
field->setCursorPosition(0);
}
void AudioDevices::init()
{
#if QT_CONFIG(permissions)
// microphone
QMicrophonePermission microphonePermission;
switch (qApp->checkPermission(microphonePermission)) {
case Qt::PermissionStatus::Undetermined:
qApp->requestPermission(microphonePermission, this, &AudioDevices::init);
return;
case Qt::PermissionStatus::Denied:
qWarning("Microphone permission is not granted!");
return;
case Qt::PermissionStatus::Granted:
break;
}
#endif
connect(modeBox, QOverload<int>::of(&QComboBox::activated), this, &AudioDevices::modeChanged);
connect(deviceBox, QOverload<int>::of(&QComboBox::activated), this,
&AudioDevices::deviceChanged);
connect(m_devices, &QMediaDevices::audioInputsChanged, this, &AudioDevices::updateAudioDevices);
connect(m_devices, &QMediaDevices::audioOutputsChanged, this,
&AudioDevices::updateAudioDevices);
modeBox->setCurrentIndex(1);
updateAudioDevices();
updateDevicePropertes();
}
void AudioDevices::updateAudioDevices()
{
QSignalBlocker blockUpdates(deviceBox);
const auto devices =
m_mode == QAudioDevice::Input ? m_devices->audioInputs() : m_devices->audioOutputs();
{
deviceBox->clear();
for (auto &deviceInfo : devices) {
QString description = deviceInfo.description();
description.replace(u"\n"_s, u" - "_s);
if (deviceInfo.isDefault())
description += " (default)";
deviceBox->addItem(description, QVariant::fromValue(deviceInfo));
}
}
// select previously selected
for (int index = 0; index != devices.size(); ++index) {
if (devices[index].id() == m_deviceInfo.id()) {
deviceBox->setCurrentIndex(index);
deviceChanged(index);
return;
}
}
// select default
for (int index = 0; index != devices.size(); ++index) {
if (devices[index].isDefault()) {
deviceBox->setCurrentIndex(index);
deviceChanged(index);
m_deviceInfo = devices[index];
return;
}
}
}
void AudioDevices::modeChanged(int idx)
{
m_mode = idx == 0 ? QAudioDevice::Input : QAudioDevice::Output;
updateAudioDevices();
deviceBox->setCurrentIndex(0);
deviceChanged(0);
}
void AudioDevices::deviceChanged(int idx)
{
if (deviceBox->count() == 0) {
channelNumberField->clear();
sampleFormatField->clear();
samplingRatesField->clear();
channelLayoutField->clear();
return;
}
// device has changed
m_deviceInfo = deviceBox->itemData(idx).value<QAudioDevice>();
updateDevicePropertes();
}
#include "moc_audiodevices.cpp"