Merge pull request #16196 from amerhifi/dev469

DEV-469: Default Audio device
This commit is contained in:
Amer 2019-09-25 16:28:00 -07:00 committed by GitHub
commit 1e94a82ee6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 301 additions and 155 deletions

View file

@ -440,13 +440,13 @@ void Audio::handlePushedToTalk(bool enabled) {
} }
} }
void Audio::setInputDevice(const QAudioDeviceInfo& device, bool isHMD) { void Audio::setInputDevice(const HifiAudioDeviceInfo& device, bool isHMD) {
withWriteLock([&] { withWriteLock([&] {
_devices.chooseInputDevice(device, isHMD); _devices.chooseInputDevice(device, isHMD);
}); });
} }
void Audio::setOutputDevice(const QAudioDeviceInfo& device, bool isHMD) { void Audio::setOutputDevice(const HifiAudioDeviceInfo& device, bool isHMD) {
withWriteLock([&] { withWriteLock([&] {
_devices.chooseOutputDevice(device, isHMD); _devices.chooseOutputDevice(device, isHMD);
}); });

View file

@ -19,6 +19,7 @@
#include "SettingHandle.h" #include "SettingHandle.h"
#include "AudioFileWav.h" #include "AudioFileWav.h"
#include <shared/ReadWriteLockable.h> #include <shared/ReadWriteLockable.h>
#include <HifiAudioDeviceInfo.h>
using MutedGetter = std::function<bool()>; using MutedGetter = std::function<bool()>;
using MutedSetter = std::function<void(bool)>; using MutedSetter = std::function<void(bool)>;
@ -158,7 +159,7 @@ public:
* @param {boolean} isHMD - Is HMD. * @param {boolean} isHMD - Is HMD.
* @deprecated This function is deprecated and will be removed. * @deprecated This function is deprecated and will be removed.
*/ */
Q_INVOKABLE void setInputDevice(const QAudioDeviceInfo& device, bool isHMD); Q_INVOKABLE void setInputDevice(const HifiAudioDeviceInfo& device, bool isHMD);
/**jsdoc /**jsdoc
* @function Audio.setOutputDevice * @function Audio.setOutputDevice
@ -166,7 +167,7 @@ public:
* @param {boolean} isHMD - Is HMD. * @param {boolean} isHMD - Is HMD.
* @deprecated This function is deprecated and will be removed. * @deprecated This function is deprecated and will be removed.
*/ */
Q_INVOKABLE void setOutputDevice(const QAudioDeviceInfo& device, bool isHMD); Q_INVOKABLE void setOutputDevice(const HifiAudioDeviceInfo& device, bool isHMD);
/**jsdoc /**jsdoc
* Enables or disables reverberation. Reverberation is done by the client on the post-mix audio. The reverberation options * Enables or disables reverberation. Reverberation is done by the client on the post-mix audio. The reverberation options

View file

@ -29,6 +29,8 @@ static Setting::Handle<QString> desktopOutputDeviceSetting { QStringList { Audio
static Setting::Handle<QString> hmdInputDeviceSetting { QStringList { Audio::AUDIO, Audio::HMD, "INPUT" }}; static Setting::Handle<QString> hmdInputDeviceSetting { QStringList { Audio::AUDIO, Audio::HMD, "INPUT" }};
static Setting::Handle<QString> hmdOutputDeviceSetting { QStringList { Audio::AUDIO, Audio::HMD, "OUTPUT" }}; static Setting::Handle<QString> hmdOutputDeviceSetting { QStringList { Audio::AUDIO, Audio::HMD, "OUTPUT" }};
Q_DECLARE_METATYPE(HifiAudioDeviceInfo);
Setting::Handle<QString>& getSetting(bool contextIsHMD, QAudio::Mode mode) { Setting::Handle<QString>& getSetting(bool contextIsHMD, QAudio::Mode mode) {
if (mode == QAudio::AudioInput) { if (mode == QAudio::AudioInput) {
return contextIsHMD ? hmdInputDeviceSetting : desktopInputDeviceSetting; return contextIsHMD ? hmdInputDeviceSetting : desktopInputDeviceSetting;
@ -64,6 +66,8 @@ static QString getTargetDevice(bool hmd, QAudio::Mode mode) {
} else { // if (_mode == QAudio::AudioOutput) } else { // if (_mode == QAudio::AudioOutput)
deviceName = qApp->getActiveDisplayPlugin()->getPreferredAudioOutDevice(); deviceName = qApp->getActiveDisplayPlugin()->getPreferredAudioOutDevice();
} }
} else {
deviceName = HifiAudioDeviceInfo::DEFAULT_DEVICE_NAME;
} }
return deviceName; return deviceName;
} }
@ -139,7 +143,7 @@ QVariant AudioDeviceList::data(const QModelIndex& index, int role) const {
} else if (role == SelectedHMDRole) { } else if (role == SelectedHMDRole) {
return _devices.at(index.row())->selectedHMD; return _devices.at(index.row())->selectedHMD;
} else if (role == InfoRole) { } else if (role == InfoRole) {
return QVariant::fromValue<QAudioDeviceInfo>(_devices.at(index.row())->info); return QVariant::fromValue<HifiAudioDeviceInfo>(_devices.at(index.row())->info);
} else { } else {
return QVariant(); return QVariant();
} }
@ -191,18 +195,13 @@ void AudioDeviceList::resetDevice(bool contextIsHMD) {
#endif #endif
} }
void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device, bool isHMD) { void AudioDeviceList::onDeviceChanged(const HifiAudioDeviceInfo& device, bool isHMD) {
QAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice; HifiAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice;
selectedDevice = device; selectedDevice = device;
for (auto i = 0; i < _devices.size(); ++i) { for (auto i = 0; i < _devices.size(); ++i) {
std::shared_ptr<AudioDevice> device = _devices[i]; std::shared_ptr<AudioDevice> device = _devices[i];
bool& isSelected = isHMD ? device->selectedHMD : device->selectedDesktop; bool& isSelected = isHMD ? device->selectedHMD : device->selectedDesktop;
if (isSelected && device->info != selectedDevice) { isSelected = device->info == selectedDevice;
isSelected = false;
} else if (device->info == selectedDevice) {
isSelected = true;
}
} }
emit deviceChanged(selectedDevice); emit deviceChanged(selectedDevice);
@ -259,37 +258,46 @@ std::shared_ptr<scripting::AudioDevice> getSimilarDevice(const QString& deviceNa
return devices[minDistanceIndex]; return devices[minDistanceIndex];
} }
void AudioDeviceList::onDevicesChanged(const QList<QAudioDeviceInfo>& devices) { void AudioDeviceList::onDevicesChanged(const QList<HifiAudioDeviceInfo>& devices) {
beginResetModel(); beginResetModel();
QList<std::shared_ptr<AudioDevice>> newDevices; QList<std::shared_ptr<AudioDevice>> newDevices;
bool hmdIsSelected = false; bool hmdIsSelected = false;
bool desktopIsSelected = false; bool desktopIsSelected = false;
foreach(const QAudioDeviceInfo& deviceInfo, devices) { foreach(const HifiAudioDeviceInfo& deviceInfo, devices) {
for (bool isHMD : {false, true}) { for (bool isHMD : {false, true}) {
auto& backupSelectedDeviceName = isHMD ? _backupSelectedHMDDeviceName : _backupSelectedDesktopDeviceName; auto& backupSelectedDeviceName = isHMD ? _backupSelectedHMDDeviceName : _backupSelectedDesktopDeviceName;
if (deviceInfo.deviceName() == backupSelectedDeviceName) { if (deviceInfo.deviceName() == backupSelectedDeviceName) {
QAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice; HifiAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice;
selectedDevice = deviceInfo; selectedDevice = deviceInfo;
backupSelectedDeviceName.clear(); backupSelectedDeviceName.clear();
} }
} }
} }
foreach(const QAudioDeviceInfo& deviceInfo, devices) { foreach(const HifiAudioDeviceInfo& deviceInfo, devices) {
AudioDevice device; AudioDevice device;
device.info = deviceInfo; device.info = deviceInfo;
device.display = device.info.deviceName()
.replace("High Definition", "HD") if (deviceInfo.isDefault()) {
.remove("Device") if (deviceInfo.getMode() == QAudio::AudioInput) {
.replace(" )", ")"); device.display = "Default microphone (recommended)";
} else {
device.display = "Default audio (recommended)";
}
} else {
device.display = device.info.deviceName()
.replace("High Definition", "HD")
.remove("Device")
.replace(" )", ")");
}
for (bool isHMD : {false, true}) { for (bool isHMD : {false, true}) {
QAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice; HifiAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice;
bool& isSelected = isHMD ? device.selectedHMD : device.selectedDesktop; bool& isSelected = isHMD ? device.selectedHMD : device.selectedDesktop;
if (!selectedDevice.isNull()) { if (!selectedDevice.getDevice().isNull()) {
isSelected = (device.info == selectedDevice); isSelected = (device.info == selectedDevice);
} }
else { else {
@ -325,13 +333,13 @@ void AudioDeviceList::onDevicesChanged(const QList<QAudioDeviceInfo>& devices) {
if (!newDevices.isEmpty()) { if (!newDevices.isEmpty()) {
if (!hmdIsSelected) { if (!hmdIsSelected) {
_backupSelectedHMDDeviceName = !_selectedHMDDevice.isNull() ? _selectedHMDDevice.deviceName() : _hmdSavedDeviceName; _backupSelectedHMDDeviceName = !_selectedHMDDevice.getDevice().isNull() ? _selectedHMDDevice.deviceName() : _hmdSavedDeviceName;
auto device = getSimilarDevice(_backupSelectedHMDDeviceName, newDevices); auto device = getSimilarDevice(_backupSelectedHMDDeviceName, newDevices);
device->selectedHMD = true; device->selectedHMD = true;
emit selectedDevicePlugged(device->info, true); emit selectedDevicePlugged(device->info, true);
} }
if (!desktopIsSelected) { if (!desktopIsSelected) {
_backupSelectedDesktopDeviceName = !_selectedDesktopDevice.isNull() ? _selectedDesktopDevice.deviceName() : _desktopSavedDeviceName; _backupSelectedDesktopDeviceName = !_selectedDesktopDevice.getDevice().isNull() ? _selectedDesktopDevice.deviceName() : _desktopSavedDeviceName;
auto device = getSimilarDevice(_backupSelectedDesktopDeviceName, newDevices); auto device = getSimilarDevice(_backupSelectedDesktopDeviceName, newDevices);
device->selectedDesktop = true; device->selectedDesktop = true;
emit selectedDevicePlugged(device->info, false); emit selectedDevicePlugged(device->info, false);
@ -382,8 +390,8 @@ AudioDevices::AudioDevices(bool& contextIsHMD) : _contextIsHMD(contextIsHMD) {
_outputs.onDeviceChanged(client->getActiveAudioDevice(QAudio::AudioOutput), contextIsHMD); _outputs.onDeviceChanged(client->getActiveAudioDevice(QAudio::AudioOutput), contextIsHMD);
// connections are made after client is initialized, so we must also fetch the devices // connections are made after client is initialized, so we must also fetch the devices
const QList<QAudioDeviceInfo>& devicesInput = client->getAudioDevices(QAudio::AudioInput); const QList<HifiAudioDeviceInfo>& devicesInput = client->getAudioDevices(QAudio::AudioInput);
const QList<QAudioDeviceInfo>& devicesOutput = client->getAudioDevices(QAudio::AudioOutput); const QList<HifiAudioDeviceInfo>& devicesOutput = client->getAudioDevices(QAudio::AudioOutput);
//setup devices //setup devices
_inputs.onDevicesChanged(devicesInput); _inputs.onDevicesChanged(devicesInput);
@ -397,9 +405,9 @@ void AudioDevices::onContextChanged(const QString& context) {
_outputs.resetDevice(_contextIsHMD); _outputs.resetDevice(_contextIsHMD);
} }
void AudioDevices::onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device, void AudioDevices::onDeviceSelected(QAudio::Mode mode, const HifiAudioDeviceInfo& device,
const QAudioDeviceInfo& previousDevice, bool isHMD) { const HifiAudioDeviceInfo& previousDevice, bool isHMD) {
QString deviceName = device.isNull() ? QString() : device.deviceName(); QString deviceName = device.deviceName();
auto& setting = getSetting(isHMD, mode); auto& setting = getSetting(isHMD, mode);
@ -410,7 +418,7 @@ void AudioDevices::onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& d
setting.set(deviceName); setting.set(deviceName);
// log the selected device // log the selected device
if (!device.isNull()) { if (!device.getDevice().isNull()) {
QJsonObject data; QJsonObject data;
const QString MODE = "audio_mode"; const QString MODE = "audio_mode";
@ -434,13 +442,13 @@ void AudioDevices::onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& d
} }
} }
void AudioDevices::onDeviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& device) { void AudioDevices::onDeviceChanged(QAudio::Mode mode, const HifiAudioDeviceInfo& device) {
if (mode == QAudio::AudioInput) { if (mode == QAudio::AudioInput) {
if (_requestedInputDevice == device) { if (_requestedInputDevice == device) {
onDeviceSelected(QAudio::AudioInput, device, onDeviceSelected(QAudio::AudioInput, device,
_contextIsHMD ? _inputs._selectedHMDDevice : _inputs._selectedDesktopDevice, _contextIsHMD ? _inputs._selectedHMDDevice : _inputs._selectedDesktopDevice,
_contextIsHMD); _contextIsHMD);
_requestedInputDevice = QAudioDeviceInfo(); _requestedInputDevice = HifiAudioDeviceInfo();
} }
_inputs.onDeviceChanged(device, _contextIsHMD); _inputs.onDeviceChanged(device, _contextIsHMD);
} else { // if (mode == QAudio::AudioOutput) } else { // if (mode == QAudio::AudioOutput)
@ -448,13 +456,13 @@ void AudioDevices::onDeviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& de
onDeviceSelected(QAudio::AudioOutput, device, onDeviceSelected(QAudio::AudioOutput, device,
_contextIsHMD ? _outputs._selectedHMDDevice : _outputs._selectedDesktopDevice, _contextIsHMD ? _outputs._selectedHMDDevice : _outputs._selectedDesktopDevice,
_contextIsHMD); _contextIsHMD);
_requestedOutputDevice = QAudioDeviceInfo(); _requestedOutputDevice = HifiAudioDeviceInfo();
} }
_outputs.onDeviceChanged(device, _contextIsHMD); _outputs.onDeviceChanged(device, _contextIsHMD);
} }
} }
void AudioDevices::onDevicesChanged(QAudio::Mode mode, const QList<QAudioDeviceInfo>& devices) { void AudioDevices::onDevicesChanged(QAudio::Mode mode, const QList<HifiAudioDeviceInfo>& devices) {
static std::once_flag once; static std::once_flag once;
std::call_once(once, [&] { std::call_once(once, [&] {
//readout settings //readout settings
@ -503,14 +511,14 @@ void AudioDevices::onDevicesChanged(QAudio::Mode mode, const QList<QAudioDeviceI
} }
void AudioDevices::chooseInputDevice(const QAudioDeviceInfo& device, bool isHMD) { void AudioDevices::chooseInputDevice(const HifiAudioDeviceInfo& device, bool isHMD) {
//check if current context equals device to change //check if current context equals device to change
if (_contextIsHMD == isHMD) { if (_contextIsHMD == isHMD) {
auto client = DependencyManager::get<AudioClient>().data(); auto client = DependencyManager::get<AudioClient>().data();
_requestedInputDevice = device; _requestedInputDevice = device;
QMetaObject::invokeMethod(client, "switchAudioDevice", QMetaObject::invokeMethod(client, "switchAudioDevice",
Q_ARG(QAudio::Mode, QAudio::AudioInput), Q_ARG(QAudio::Mode, QAudio::AudioInput),
Q_ARG(const QAudioDeviceInfo&, device)); Q_ARG(const HifiAudioDeviceInfo&, device));
} else { } else {
//context is different. just save device in settings //context is different. just save device in settings
onDeviceSelected(QAudio::AudioInput, device, onDeviceSelected(QAudio::AudioInput, device,
@ -520,14 +528,14 @@ void AudioDevices::chooseInputDevice(const QAudioDeviceInfo& device, bool isHMD)
} }
} }
void AudioDevices::chooseOutputDevice(const QAudioDeviceInfo& device, bool isHMD) { void AudioDevices::chooseOutputDevice(const HifiAudioDeviceInfo& device, bool isHMD) {
//check if current context equals device to change //check if current context equals device to change
if (_contextIsHMD == isHMD) { if (_contextIsHMD == isHMD) {
auto client = DependencyManager::get<AudioClient>().data(); auto client = DependencyManager::get<AudioClient>().data();
_requestedOutputDevice = device; _requestedOutputDevice = device;
QMetaObject::invokeMethod(client, "switchAudioDevice", QMetaObject::invokeMethod(client, "switchAudioDevice",
Q_ARG(QAudio::Mode, QAudio::AudioOutput), Q_ARG(QAudio::Mode, QAudio::AudioOutput),
Q_ARG(const QAudioDeviceInfo&, device)); Q_ARG(const HifiAudioDeviceInfo&, device));
} else { } else {
//context is different. just save device in settings //context is different. just save device in settings
onDeviceSelected(QAudio::AudioOutput, device, onDeviceSelected(QAudio::AudioOutput, device,

View file

@ -19,11 +19,13 @@
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QAudioDeviceInfo> #include <QAudioDeviceInfo>
#include <HifiAudioDeviceInfo.h>
namespace scripting { namespace scripting {
class AudioDevice { class AudioDevice {
public: public:
QAudioDeviceInfo info; HifiAudioDeviceInfo info;
QString display; QString display;
bool selectedDesktop { false }; bool selectedDesktop { false };
bool selectedHMD { false }; bool selectedHMD { false };
@ -50,12 +52,12 @@ public:
void resetDevice(bool contextIsHMD); void resetDevice(bool contextIsHMD);
signals: signals:
void deviceChanged(const QAudioDeviceInfo& device); void deviceChanged(const HifiAudioDeviceInfo& device);
void selectedDevicePlugged(const QAudioDeviceInfo& device, bool isHMD); void selectedDevicePlugged(const HifiAudioDeviceInfo& device, bool isHMD);
protected slots: protected slots:
void onDeviceChanged(const QAudioDeviceInfo& device, bool isHMD); void onDeviceChanged(const HifiAudioDeviceInfo& device, bool isHMD);
void onDevicesChanged(const QList<QAudioDeviceInfo>& devices); void onDevicesChanged(const QList<HifiAudioDeviceInfo>& devices);
protected: protected:
friend class AudioDevices; friend class AudioDevices;
@ -63,8 +65,8 @@ protected:
static QHash<int, QByteArray> _roles; static QHash<int, QByteArray> _roles;
static Qt::ItemFlags _flags; static Qt::ItemFlags _flags;
const QAudio::Mode _mode; const QAudio::Mode _mode;
QAudioDeviceInfo _selectedDesktopDevice; HifiAudioDeviceInfo _selectedDesktopDevice;
QAudioDeviceInfo _selectedHMDDevice; HifiAudioDeviceInfo _selectedHMDDevice;
QString _backupSelectedDesktopDeviceName; QString _backupSelectedDesktopDeviceName;
QString _backupSelectedHMDDeviceName; QString _backupSelectedHMDDeviceName;
QList<std::shared_ptr<AudioDevice>> _devices; QList<std::shared_ptr<AudioDevice>> _devices;
@ -124,14 +126,14 @@ signals:
void nop(); void nop();
private slots: private slots:
void chooseInputDevice(const QAudioDeviceInfo& device, bool isHMD); void chooseInputDevice(const HifiAudioDeviceInfo& device, bool isHMD);
void chooseOutputDevice(const QAudioDeviceInfo& device, bool isHMD); void chooseOutputDevice(const HifiAudioDeviceInfo& device, bool isHMD);
void onContextChanged(const QString& context); void onContextChanged(const QString& context);
void onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device, void onDeviceSelected(QAudio::Mode mode, const HifiAudioDeviceInfo& device,
const QAudioDeviceInfo& previousDevice, bool isHMD); const HifiAudioDeviceInfo& previousDevice, bool isHMD);
void onDeviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& device); void onDeviceChanged(QAudio::Mode mode, const HifiAudioDeviceInfo& device);
void onDevicesChanged(QAudio::Mode mode, const QList<QAudioDeviceInfo>& devices); void onDevicesChanged(QAudio::Mode mode, const QList<HifiAudioDeviceInfo>& devices);
private: private:
friend class Audio; friend class Audio;
@ -141,8 +143,8 @@ private:
AudioInputDeviceList _inputs; AudioInputDeviceList _inputs;
AudioDeviceList _outputs { QAudio::AudioOutput }; AudioDeviceList _outputs { QAudio::AudioOutput };
QAudioDeviceInfo _requestedOutputDevice; HifiAudioDeviceInfo _requestedOutputDevice;
QAudioDeviceInfo _requestedInputDevice; HifiAudioDeviceInfo _requestedInputDevice;
const bool& _contextIsHMD; const bool& _contextIsHMD;
}; };

View file

@ -91,11 +91,23 @@ using Lock = std::unique_lock<Mutex>;
Mutex _deviceMutex; Mutex _deviceMutex;
Mutex _recordMutex; Mutex _recordMutex;
HifiAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode);
// thread-safe // thread-safe
QList<QAudioDeviceInfo> getAvailableDevices(QAudio::Mode mode) { QList<HifiAudioDeviceInfo> getAvailableDevices(QAudio::Mode mode) {
// NOTE: availableDevices() clobbers the Qt internal device list // NOTE: availableDevices() clobbers the Qt internal device list
Lock lock(_deviceMutex); Lock lock(_deviceMutex);
return QAudioDeviceInfo::availableDevices(mode); auto devices = QAudioDeviceInfo::availableDevices(mode);
QList<HifiAudioDeviceInfo> newDevices;
for (auto& device : devices) {
newDevices.push_back(HifiAudioDeviceInfo(device, false, mode));
}
newDevices.push_front(defaultAudioDeviceForMode(mode));
return newDevices;
} }
// now called from a background thread, to keep blocking operations off the audio thread // now called from a background thread, to keep blocking operations off the audio thread
@ -109,6 +121,9 @@ void AudioClient::checkDevices() {
auto inputDevices = getAvailableDevices(QAudio::AudioInput); auto inputDevices = getAvailableDevices(QAudio::AudioInput);
auto outputDevices = getAvailableDevices(QAudio::AudioOutput); auto outputDevices = getAvailableDevices(QAudio::AudioOutput);
QMetaObject::invokeMethod(this, "changeDefault", Q_ARG(HifiAudioDeviceInfo, inputDevices.first()), Q_ARG(QAudio::Mode, QAudio::AudioInput));
QMetaObject::invokeMethod(this, "changeDefault", Q_ARG(HifiAudioDeviceInfo, outputDevices.first()), Q_ARG(QAudio::Mode, QAudio::AudioOutput));
Lock lock(_deviceMutex); Lock lock(_deviceMutex);
if (inputDevices != _inputDevices) { if (inputDevices != _inputDevices) {
@ -122,22 +137,22 @@ void AudioClient::checkDevices() {
} }
} }
QAudioDeviceInfo AudioClient::getActiveAudioDevice(QAudio::Mode mode) const { HifiAudioDeviceInfo AudioClient::getActiveAudioDevice(QAudio::Mode mode) const {
Lock lock(_deviceMutex); Lock lock(_deviceMutex);
if (mode == QAudio::AudioInput) { if (mode == QAudio::AudioInput) {
return _inputDeviceInfo; return _inputDeviceInfo;
} else { // if (mode == QAudio::AudioOutput) } else {
return _outputDeviceInfo; return _outputDeviceInfo;
} }
} }
QList<QAudioDeviceInfo> AudioClient::getAudioDevices(QAudio::Mode mode) const { QList<HifiAudioDeviceInfo> AudioClient::getAudioDevices(QAudio::Mode mode) const {
Lock lock(_deviceMutex); Lock lock(_deviceMutex);
if (mode == QAudio::AudioInput) { if (mode == QAudio::AudioInput) {
return _inputDevices; return _inputDevices;
} else { // if (mode == QAudio::AudioOutput) } else {
return _outputDevices; return _outputDevices;
} }
} }
@ -226,7 +241,7 @@ static float computeLoudness(int16_t* samples, int numSamples) {
template <int NUM_CHANNELS> template <int NUM_CHANNELS>
static void applyGainSmoothing(float* buffer, int numFrames, float gain0, float gain1) { static void applyGainSmoothing(float* buffer, int numFrames, float gain0, float gain1) {
// fast path for unity gain // fast path for unity gain
if (gain0 == 1.0f && gain1 == 1.0f) { if (gain0 == 1.0f && gain1 == 1.0f) {
return; return;
@ -241,7 +256,7 @@ static void applyGainSmoothing(float* buffer, int numFrames, float gain0, float
float tStep = 1.0f / numFrames; float tStep = 1.0f / numFrames;
for (int i = 0; i < numFrames; i++) { for (int i = 0; i < numFrames; i++) {
// evaluate poly over t=[0,1) // evaluate poly over t=[0,1)
float gain = (c3 * t + c2) * t * t + c0; float gain = (c3 * t + c2) * t * t + c0;
t += tStep; t += tStep;
@ -321,9 +336,9 @@ AudioClient::AudioClient() :
} }
connect(&_receivedAudioStream, &MixedProcessedAudioStream::processSamples, connect(&_receivedAudioStream, &MixedProcessedAudioStream::processSamples,
this, &AudioClient::processReceivedSamples, Qt::DirectConnection); this, &AudioClient::processReceivedSamples, Qt::DirectConnection);
connect(this, &AudioClient::changeDevice, this, [=](const QAudioDeviceInfo& outputDeviceInfo) { connect(this, &AudioClient::changeDevice, this, [=](const HifiAudioDeviceInfo& outputDeviceInfo) {
qCDebug(audioclient) << "got AudioClient::changeDevice signal, about to call switchOutputToAudioDevice() outputDeviceInfo: [" << outputDeviceInfo.deviceName() << "]"; qCDebug(audioclient)<< "got AudioClient::changeDevice signal, about to call switchOutputToAudioDevice() outputDeviceInfo: ["<< outputDeviceInfo.deviceName() << "]";
switchOutputToAudioDevice(outputDeviceInfo); switchOutputToAudioDevice(outputDeviceInfo);
}); });
@ -373,7 +388,7 @@ AudioClient::AudioClient() :
packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat"); packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat");
auto& domainHandler = nodeList->getDomainHandler(); auto& domainHandler = nodeList->getDomainHandler();
connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, [this] { connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, [this] {
_solo.reset(); _solo.reset();
}); });
connect(nodeList.data(), &NodeList::nodeActivated, this, [this](SharedNodePointer node) { connect(nodeList.data(), &NodeList::nodeActivated, this, [this](SharedNodePointer node) {
@ -431,15 +446,14 @@ void AudioClient::setAudioPaused(bool pause) {
} }
} }
QAudioDeviceInfo getNamedAudioDeviceForMode(QAudio::Mode mode, const QString& deviceName) { HifiAudioDeviceInfo getNamedAudioDeviceForMode(QAudio::Mode mode, const QString& deviceName) {
QAudioDeviceInfo result; HifiAudioDeviceInfo result;
foreach(QAudioDeviceInfo audioDevice, getAvailableDevices(mode)) { foreach (HifiAudioDeviceInfo audioDevice, getAvailableDevices(mode)) {
if (audioDevice.deviceName().trimmed() == deviceName.trimmed()) { if (audioDevice.deviceName().trimmed() == deviceName.trimmed()) {
result = audioDevice; result = audioDevice;
break; break;
} }
} }
return result; return result;
} }
@ -487,9 +501,11 @@ QString AudioClient::getWinDeviceName(wchar_t* guid) {
#endif #endif
QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { HifiAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
QList<QAudioDeviceInfo> devices = QAudioDeviceInfo::availableDevices(mode);
#ifdef __APPLE__ #ifdef __APPLE__
if (getAvailableDevices(mode).size() > 1) { if (devices.size() > 1) {
AudioDeviceID defaultDeviceID = 0; AudioDeviceID defaultDeviceID = 0;
uint32_t propertySize = sizeof(AudioDeviceID); uint32_t propertySize = sizeof(AudioDeviceID);
AudioObjectPropertyAddress propertyAddress = { AudioObjectPropertyAddress propertyAddress = {
@ -519,9 +535,9 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
if (!getPropertyError && propertySize) { if (!getPropertyError && propertySize) {
// find a device in the list that matches the name we have and return it // find a device in the list that matches the name we have and return it
foreach(QAudioDeviceInfo audioDevice, getAvailableDevices(mode)) { foreach(QAudioDeviceInfo audioDevice, devices){
if (audioDevice.deviceName() == CFStringGetCStringPtr(deviceName, kCFStringEncodingMacRoman)) { if (audioDevice.deviceName() == CFStringGetCStringPtr(deviceName, kCFStringEncodingMacRoman)) {
return audioDevice; return HifiAudioDeviceInfo(audioDevice, true, mode);
} }
} }
} }
@ -569,10 +585,18 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
CoUninitialize(); CoUninitialize();
} }
qCDebug(audioclient) << "defaultAudioDeviceForMode mode: " << (mode == QAudio::AudioOutput ? "Output" : "Input") HifiAudioDeviceInfo foundDevice;
<< " [" << deviceName << "] [" << getNamedAudioDeviceForMode(mode, deviceName).deviceName() << "]"; foreach(QAudioDeviceInfo audioDevice, devices) {
if (audioDevice.deviceName().trimmed() == deviceName.trimmed()) {
foundDevice=HifiAudioDeviceInfo(audioDevice,true,mode);
break;
}
}
qCDebug(audioclient) << "defaultAudioDeviceForMode mode: " << (mode == QAudio::AudioOutput ? "Output" : "Input")
<< " [" << deviceName << "] [" << foundDevice.deviceName() << "]";
return getNamedAudioDeviceForMode(mode, deviceName); return foundDevice;
#endif #endif
#if defined (Q_OS_ANDROID) #if defined (Q_OS_ANDROID)
@ -580,18 +604,18 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
Setting::Handle<bool> enableAEC(SETTING_AEC_KEY, DEFAULT_AEC_ENABLED); Setting::Handle<bool> enableAEC(SETTING_AEC_KEY, DEFAULT_AEC_ENABLED);
bool aecEnabled = enableAEC.get(); bool aecEnabled = enableAEC.get();
auto audioClient = DependencyManager::get<AudioClient>(); auto audioClient = DependencyManager::get<AudioClient>();
bool headsetOn = audioClient? audioClient->isHeadsetPluggedIn() : false; bool headsetOn = audioClient ? audioClient->isHeadsetPluggedIn() : false;
auto inputDevices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); for (QAudioDeviceInfo inputDevice : devices) {
for (auto inputDevice : inputDevices) {
if (((headsetOn || !aecEnabled) && inputDevice.deviceName() == VOICE_RECOGNITION) || if (((headsetOn || !aecEnabled) && inputDevice.deviceName() == VOICE_RECOGNITION) ||
((!headsetOn && aecEnabled) && inputDevice.deviceName() == VOICE_COMMUNICATION)) { ((!headsetOn && aecEnabled) && inputDevice.deviceName() == VOICE_COMMUNICATION)) {
return inputDevice; return HifiAudioDeviceInfo(inputDevice, false, QAudio::AudioInput);
} }
} }
} }
#endif #endif
// fallback for failed lookup is the default device // fallback for failed lookup is the default device
return (mode == QAudio::AudioInput) ? QAudioDeviceInfo::defaultInputDevice() : QAudioDeviceInfo::defaultOutputDevice(); return (mode == QAudio::AudioInput) ? HifiAudioDeviceInfo(QAudioDeviceInfo::defaultInputDevice(), true,mode) :
HifiAudioDeviceInfo(QAudioDeviceInfo::defaultOutputDevice(), true, mode);
} }
bool AudioClient::getNamedAudioDeviceForModeExists(QAudio::Mode mode, const QString& deviceName) { bool AudioClient::getNamedAudioDeviceForModeExists(QAudio::Mode mode, const QString& deviceName) {
@ -643,29 +667,29 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice,
if (IsWindows8OrGreater()) { if (IsWindows8OrGreater()) {
// On Windows using WASAPI shared-mode, returns the internal mix format // On Windows using WASAPI shared-mode, returns the internal mix format
return nativeFormatForAudioDevice(audioDevice, adjustedAudioFormat); return nativeFormatForAudioDevice(audioDevice, adjustedAudioFormat);
} // else enumerate formats } // else enumerate formats
#endif #endif
#if defined(Q_OS_MAC) #if defined(Q_OS_MAC)
// Mac OSX returns the preferred CoreAudio format // Mac OSX returns the preferred CoreAudio format
return nativeFormatForAudioDevice(audioDevice, adjustedAudioFormat); return nativeFormatForAudioDevice(audioDevice, adjustedAudioFormat);
#endif #endif
#if defined(Q_OS_ANDROID) #if defined(Q_OS_ANDROID)
// As of Qt5.6, Android returns the native OpenSLES sample rate when possible, else 48000 // As of Qt5.6, Android returns the native OpenSLES sample rate when possible, else 48000
if (nativeFormatForAudioDevice(audioDevice, adjustedAudioFormat)) { if (nativeFormatForAudioDevice(audioDevice, adjustedAudioFormat)) {
return true; return true;
} // else enumerate formats } // else enumerate formats
#endif #endif
adjustedAudioFormat = desiredAudioFormat; adjustedAudioFormat = desiredAudioFormat;
// //
// Attempt the device sample rate and channel count in decreasing order of preference. // Attempt the device sample rate and channel count in decreasing order of preference.
// //
const int sampleRates[] = { 48000, 44100, 32000, 24000, 16000, 96000, 192000, 88200, 176400 }; const int sampleRates[] = { 48000, 44100, 32000, 24000, 16000, 96000, 192000, 88200, 176400 };
const int inputChannels[] = { 1, 2, 4, 6, 8 }; // prefer mono const int inputChannels[] = { 1, 2, 4, 6, 8 }; // prefer mono
const int outputChannels[] = { 2, 4, 6, 8, 1 }; // prefer stereo, downmix as last resort const int outputChannels[] = { 2, 4, 6, 8, 1 }; // prefer stereo, downmix as last resort
for (int channelCount : (desiredAudioFormat.channelCount() == 1 ? inputChannels : outputChannels)) { for (int channelCount : (desiredAudioFormat.channelCount() == 1 ? inputChannels : outputChannels)) {
for (int sampleRate : sampleRates) { for (int sampleRate : sampleRates) {
@ -758,22 +782,22 @@ void AudioClient::start() {
_desiredOutputFormat = _desiredInputFormat; _desiredOutputFormat = _desiredInputFormat;
_desiredOutputFormat.setChannelCount(OUTPUT_CHANNEL_COUNT); _desiredOutputFormat.setChannelCount(OUTPUT_CHANNEL_COUNT);
QAudioDeviceInfo inputDeviceInfo = defaultAudioDeviceForMode(QAudio::AudioInput); HifiAudioDeviceInfo inputDeviceInfo = defaultAudioDeviceForMode(QAudio::AudioInput);
qCDebug(audioclient) << "The default audio input device is" << inputDeviceInfo.deviceName(); qCDebug(audioclient) << "The default audio input device is" << inputDeviceInfo.deviceName();
bool inputFormatSupported = switchInputToAudioDevice(inputDeviceInfo); bool inputFormatSupported = switchInputToAudioDevice(inputDeviceInfo);
QAudioDeviceInfo outputDeviceInfo = defaultAudioDeviceForMode(QAudio::AudioOutput); HifiAudioDeviceInfo outputDeviceInfo = defaultAudioDeviceForMode(QAudio::AudioOutput);
qCDebug(audioclient) << "The default audio output device is" << outputDeviceInfo.deviceName(); qCDebug(audioclient) << "The default audio output device is" << outputDeviceInfo.deviceName();
bool outputFormatSupported = switchOutputToAudioDevice(outputDeviceInfo); bool outputFormatSupported = switchOutputToAudioDevice(outputDeviceInfo);
if (!inputFormatSupported) { if (!inputFormatSupported) {
qCDebug(audioclient) << "Unable to set up audio input because of a problem with input format."; qCDebug(audioclient) << "Unable to set up audio input because of a problem with input format.";
qCDebug(audioclient) << "The closest format available is" << inputDeviceInfo.nearestFormat(_desiredInputFormat); qCDebug(audioclient) << "The closest format available is" << inputDeviceInfo.getDevice().nearestFormat(_desiredInputFormat);
} }
if (!outputFormatSupported) { if (!outputFormatSupported) {
qCDebug(audioclient) << "Unable to set up audio output because of a problem with output format."; qCDebug(audioclient) << "Unable to set up audio output because of a problem with output format.";
qCDebug(audioclient) << "The closest format available is" << outputDeviceInfo.nearestFormat(_desiredOutputFormat); qCDebug(audioclient) << "The closest format available is" << outputDeviceInfo.getDevice().nearestFormat(_desiredOutputFormat);
} }
#if defined(Q_OS_ANDROID) #if defined(Q_OS_ANDROID)
connect(&_checkInputTimer, &QTimer::timeout, this, &AudioClient::checkInputTimeout); connect(&_checkInputTimer, &QTimer::timeout, this, &AudioClient::checkInputTimeout);
@ -783,10 +807,10 @@ void AudioClient::start() {
void AudioClient::stop() { void AudioClient::stop() {
qCDebug(audioclient) << "AudioClient::stop(), requesting switchInputToAudioDevice() to shut down"; qCDebug(audioclient) << "AudioClient::stop(), requesting switchInputToAudioDevice() to shut down";
switchInputToAudioDevice(QAudioDeviceInfo(), true); switchInputToAudioDevice(HifiAudioDeviceInfo(), true);
qCDebug(audioclient) << "AudioClient::stop(), requesting switchOutputToAudioDevice() to shut down"; qCDebug(audioclient) << "AudioClient::stop(), requesting switchOutputToAudioDevice() to shut down";
switchOutputToAudioDevice(QAudioDeviceInfo(), true); switchOutputToAudioDevice(HifiAudioDeviceInfo(), true);
// Stop triggering the checks // Stop triggering the checks
QObject::disconnect(_checkPeakValuesTimer, &QTimer::timeout, nullptr, nullptr); QObject::disconnect(_checkPeakValuesTimer, &QTimer::timeout, nullptr, nullptr);
@ -978,16 +1002,18 @@ void AudioClient::selectAudioFormat(const QString& selectedCodecName) {
} }
bool AudioClient::switchAudioDevice(QAudio::Mode mode, const QAudioDeviceInfo& deviceInfo) { void AudioClient::changeDefault(HifiAudioDeviceInfo newDefault, QAudio::Mode mode) {
auto device = deviceInfo; HifiAudioDeviceInfo currentDevice = mode == QAudio::AudioInput ? _inputDeviceInfo : _outputDeviceInfo;
if (currentDevice.isDefault() && currentDevice.getDevice() != newDefault.getDevice()) {
if (device.isNull()) { switchAudioDevice(mode, newDefault);
device = defaultAudioDeviceForMode(mode);
} }
}
bool AudioClient::switchAudioDevice(QAudio::Mode mode, const HifiAudioDeviceInfo& deviceInfo) {
auto device = deviceInfo;
if (mode == QAudio::AudioInput) { if (mode == QAudio::AudioInput) {
return switchInputToAudioDevice(device); return switchInputToAudioDevice(device);
} else { // if (mode == QAudio::AudioOutput) } else {
return switchOutputToAudioDevice(device); return switchOutputToAudioDevice(device);
} }
} }
@ -1030,7 +1056,7 @@ void AudioClient::configureReverb() {
p.wetDryMix = 100.0f; p.wetDryMix = 100.0f;
p.preDelay = 0.0f; p.preDelay = 0.0f;
p.earlyGain = -96.0f; // disable ER p.earlyGain = -96.0f; // disable ER
p.lateGain += _reverbOptions->getWetDryMix() * (24.0f/100.0f) - 24.0f; // -0dB to -24dB, based on wetDryMix p.lateGain += _reverbOptions->getWetDryMix() * (24.0f / 100.0f) - 24.0f; // -0dB to -24dB, based on wetDryMix
p.lateMixLeft = 0.0f; p.lateMixLeft = 0.0f;
p.lateMixRight = 0.0f; p.lateMixRight = 0.0f;
@ -1414,7 +1440,7 @@ void AudioClient::handleMicAudioInput() {
} else if (_timeSinceLastClip >= 0.0f) { } else if (_timeSinceLastClip >= 0.0f) {
_timeSinceLastClip += AudioConstants::NETWORK_FRAME_SECS; _timeSinceLastClip += AudioConstants::NETWORK_FRAME_SECS;
} }
isClipping = (_timeSinceLastClip >= 0.0f) && (_timeSinceLastClip < 2.0f); // 2 second hold time isClipping = (_timeSinceLastClip >= 0.0f) && (_timeSinceLastClip < 2.0f); // 2 second hold time
#if defined(WEBRTC_ENABLED) #if defined(WEBRTC_ENABLED)
if (_isAECEnabled) { if (_isAECEnabled) {
@ -1453,7 +1479,7 @@ void AudioClient::handleDummyAudioInput() {
? AudioConstants::NETWORK_FRAME_BYTES_STEREO ? AudioConstants::NETWORK_FRAME_BYTES_STEREO
: AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; : AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL;
QByteArray audioBuffer(numNetworkBytes, 0); // silent QByteArray audioBuffer(numNetworkBytes, 0); // silent
handleAudioInput(audioBuffer); handleAudioInput(audioBuffer);
} }
@ -1598,7 +1624,7 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) {
// direct mix into mixBuffer // direct mix into mixBuffer
injector->getLocalHRTF().mixStereo(_localScratchBuffer, mixBuffer, gain, injector->getLocalHRTF().mixStereo(_localScratchBuffer, mixBuffer, gain,
AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
} else { // injector is mono } else { // injector is mono
if (options.positionSet) { if (options.positionSet) {
@ -1736,7 +1762,7 @@ void AudioClient::setAcousticEchoCancellation(bool enable, bool emitSignal) {
bool AudioClient::setIsStereoInput(bool isStereoInput) { bool AudioClient::setIsStereoInput(bool isStereoInput) {
bool stereoInputChanged = false; bool stereoInputChanged = false;
if (isStereoInput != _isStereoInput && _inputDeviceInfo.supportedChannelCounts().contains(2)) { if (isStereoInput != _isStereoInput && _inputDeviceInfo.getDevice().supportedChannelCounts().contains(2)) {
_isStereoInput = isStereoInput; _isStereoInput = isStereoInput;
stereoInputChanged = true; stereoInputChanged = true;
@ -1798,17 +1824,17 @@ void AudioClient::outputFormatChanged() {
_receivedAudioStream.outputFormatChanged(_outputFormat.sampleRate(), OUTPUT_CHANNEL_COUNT); _receivedAudioStream.outputFormatChanged(_outputFormat.sampleRate(), OUTPUT_CHANNEL_COUNT);
} }
bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo inputDeviceInfo, bool isShutdownRequest) { bool AudioClient::switchInputToAudioDevice(const HifiAudioDeviceInfo inputDeviceInfo, bool isShutdownRequest) {
Q_ASSERT_X(QThread::currentThread() == thread(), Q_FUNC_INFO, "Function invoked on wrong thread"); Q_ASSERT_X(QThread::currentThread() == thread(), Q_FUNC_INFO, "Function invoked on wrong thread");
qCDebug(audioclient) << __FUNCTION__ << "inputDeviceInfo: [" << inputDeviceInfo.deviceName() << "]"; qCDebug(audioclient) << __FUNCTION__ << "inputDeviceInfo: [" << _inputDeviceInfo.deviceName() <<"----"<<inputDeviceInfo.getDevice().deviceName() << "]";
bool supportedFormat = false; bool supportedFormat = false;
// NOTE: device start() uses the Qt internal device list // NOTE: device start() uses the Qt internal device list
Lock lock(_deviceMutex); Lock lock(_deviceMutex);
#if defined(Q_OS_ANDROID) #if defined(Q_OS_ANDROID)
_shouldRestartInputSetup = false; // avoid a double call to _audioInput->start() from audioInputStateChanged _shouldRestartInputSetup = false; // avoid a double call to _audioInput->start() from audioInputStateChanged
#endif #endif
// cleanup any previously initialized device // cleanup any previously initialized device
@ -1822,8 +1848,6 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo inputDeviceInf
_audioInput->deleteLater(); _audioInput->deleteLater();
_audioInput = NULL; _audioInput = NULL;
_numInputCallbackBytes = 0; _numInputCallbackBytes = 0;
_inputDeviceInfo = QAudioDeviceInfo();
} }
if (_dummyAudioInput) { if (_dummyAudioInput) {
@ -1854,12 +1878,16 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo inputDeviceInf
return true; return true;
} }
if (!inputDeviceInfo.isNull()) { if (!inputDeviceInfo.getDevice().isNull()) {
qCDebug(audioclient) << "The audio input device " << inputDeviceInfo.deviceName() << "is available."; qCDebug(audioclient) << "The audio input device " << inputDeviceInfo.deviceName() << "is available.";
bool doEmit = _inputDeviceInfo.deviceName() != inputDeviceInfo.deviceName();
_inputDeviceInfo = inputDeviceInfo; _inputDeviceInfo = inputDeviceInfo;
emit deviceChanged(QAudio::AudioInput, inputDeviceInfo); if (doEmit) {
emit deviceChanged(QAudio::AudioInput, _inputDeviceInfo);
}
if (adjustedFormatForAudioDevice(inputDeviceInfo, _desiredInputFormat, _inputFormat)) { if (adjustedFormatForAudioDevice(_inputDeviceInfo.getDevice(), _desiredInputFormat, _inputFormat)) {
qCDebug(audioclient) << "The format to be used for audio input is" << _inputFormat; qCDebug(audioclient) << "The format to be used for audio input is" << _inputFormat;
// we've got the best we can get for input // we've got the best we can get for input
@ -1884,7 +1912,7 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo inputDeviceInf
// if the user wants stereo but this device can't provide then bail // if the user wants stereo but this device can't provide then bail
if (!_isStereoInput || _inputFormat.channelCount() == 2) { if (!_isStereoInput || _inputFormat.channelCount() == 2) {
_audioInput = new QAudioInput(inputDeviceInfo, _inputFormat, this); _audioInput = new QAudioInput(_inputDeviceInfo.getDevice(), _inputFormat, this);
_numInputCallbackBytes = calculateNumberOfInputCallbackBytes(_inputFormat); _numInputCallbackBytes = calculateNumberOfInputCallbackBytes(_inputFormat);
_audioInput->setBufferSize(_numInputCallbackBytes); _audioInput->setBufferSize(_numInputCallbackBytes);
// different audio input devices may have different volumes // different audio input devices may have different volumes
@ -1906,7 +1934,7 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo inputDeviceInf
connect(_inputDevice, SIGNAL(readyRead()), this, SLOT(handleMicAudioInput())); connect(_inputDevice, SIGNAL(readyRead()), this, SLOT(handleMicAudioInput()));
supportedFormat = true; supportedFormat = true;
} else { } else {
qCDebug(audioclient) << "Error starting audio input -" << _audioInput->error(); qCDebug(audioclient) << "Error starting audio input -" << _audioInput->error();
_audioInput->deleteLater(); _audioInput->deleteLater();
_audioInput = NULL; _audioInput = NULL;
} }
@ -1919,7 +1947,7 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo inputDeviceInf
// This enables clients without a mic to still receive an audio stream from the mixer. // This enables clients without a mic to still receive an audio stream from the mixer.
if (!_audioInput) { if (!_audioInput) {
qCDebug(audioclient) << "Audio input device is not available, using dummy input."; qCDebug(audioclient) << "Audio input device is not available, using dummy input.";
_inputDeviceInfo = QAudioDeviceInfo(); _inputDeviceInfo.setDevice(QAudioDeviceInfo());
emit deviceChanged(QAudio::AudioInput, _inputDeviceInfo); emit deviceChanged(QAudio::AudioInput, _inputDeviceInfo);
_inputFormat = _desiredInputFormat; _inputFormat = _desiredInputFormat;
@ -1975,11 +2003,11 @@ void AudioClient::checkInputTimeout() {
void AudioClient::setHeadsetPluggedIn(bool pluggedIn) { void AudioClient::setHeadsetPluggedIn(bool pluggedIn) {
#if defined(Q_OS_ANDROID) #if defined(Q_OS_ANDROID)
if (pluggedIn == !_isHeadsetPluggedIn && !_inputDeviceInfo.isNull()) { if (pluggedIn == !_isHeadsetPluggedIn && !_inputDeviceInfo.getDevice().isNull()) {
QAndroidJniObject brand = QAndroidJniObject::getStaticObjectField<jstring>("android/os/Build", "BRAND"); QAndroidJniObject brand = QAndroidJniObject::getStaticObjectField<jstring>("android/os/Build", "BRAND");
// some samsung phones needs more time to shutdown the previous input device // some samsung phones needs more time to shutdown the previous input device
if (brand.toString().contains("samsung", Qt::CaseInsensitive)) { if (brand.toString().contains("samsung", Qt::CaseInsensitive)) {
switchInputToAudioDevice(QAudioDeviceInfo(), true); switchInputToAudioDevice(HifiAudioDeviceInfo(), true);
QThread::msleep(200); QThread::msleep(200);
} }
@ -2026,7 +2054,7 @@ void AudioClient::outputNotify() {
} }
} }
bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo outputDeviceInfo, bool isShutdownRequest) { bool AudioClient::switchOutputToAudioDevice(const HifiAudioDeviceInfo outputDeviceInfo, bool isShutdownRequest) {
Q_ASSERT_X(QThread::currentThread() == thread(), Q_FUNC_INFO, "Function invoked on wrong thread"); Q_ASSERT_X(QThread::currentThread() == thread(), Q_FUNC_INFO, "Function invoked on wrong thread");
qCDebug(audioclient) << "AudioClient::switchOutputToAudioDevice() outputDeviceInfo: [" << outputDeviceInfo.deviceName() << "]"; qCDebug(audioclient) << "AudioClient::switchOutputToAudioDevice() outputDeviceInfo: [" << outputDeviceInfo.deviceName() << "]";
@ -2061,8 +2089,6 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo outputDeviceI
delete[] _localOutputMixBuffer; delete[] _localOutputMixBuffer;
_localOutputMixBuffer = NULL; _localOutputMixBuffer = NULL;
_outputDeviceInfo = QAudioDeviceInfo();
} }
// cleanup any resamplers // cleanup any resamplers
@ -2086,12 +2112,15 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo outputDeviceI
return true; return true;
} }
if (!outputDeviceInfo.isNull()) { if (!outputDeviceInfo.getDevice().isNull()) {
qCDebug(audioclient) << "The audio output device " << outputDeviceInfo.deviceName() << "is available."; qCDebug(audioclient) << "The audio output device " << outputDeviceInfo.deviceName() << "is available.";
bool doEmit = _outputDeviceInfo.deviceName() != outputDeviceInfo.deviceName();
_outputDeviceInfo = outputDeviceInfo; _outputDeviceInfo = outputDeviceInfo;
emit deviceChanged(QAudio::AudioOutput, outputDeviceInfo); if (doEmit) {
emit deviceChanged(QAudio::AudioOutput, _outputDeviceInfo);
}
if (adjustedFormatForAudioDevice(outputDeviceInfo, _desiredOutputFormat, _outputFormat)) { if (adjustedFormatForAudioDevice(_outputDeviceInfo.getDevice(), _desiredOutputFormat, _outputFormat)) {
qCDebug(audioclient) << "The format to be used for audio output is" << _outputFormat; qCDebug(audioclient) << "The format to be used for audio output is" << _outputFormat;
// we've got the best we can get for input // we've got the best we can get for input
@ -2113,7 +2142,7 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo outputDeviceI
outputFormatChanged(); outputFormatChanged();
// setup our general output device for audio-mixer audio // setup our general output device for audio-mixer audio
_audioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this); _audioOutput = new QAudioOutput(_outputDeviceInfo.getDevice(), _outputFormat, this);
int deviceChannelCount = _outputFormat.channelCount(); int deviceChannelCount = _outputFormat.channelCount();
int frameSize = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * deviceChannelCount * _outputFormat.sampleRate()) / _desiredOutputFormat.sampleRate(); int frameSize = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * deviceChannelCount * _outputFormat.sampleRate()) / _desiredOutputFormat.sampleRate();
@ -2165,7 +2194,7 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo outputDeviceI
localAudioLock.unlock(); localAudioLock.unlock();
// setup a loopback audio output device // setup a loopback audio output device
_loopbackAudioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this); _loopbackAudioOutput = new QAudioOutput(outputDeviceInfo.getDevice(), _outputFormat, this);
_timeSinceLastReceived.start(); _timeSinceLastReceived.start();
@ -2239,7 +2268,7 @@ float AudioClient::azimuthForSource(const glm::vec3& relativePosition) {
// produce an oriented angle about the y-axis // produce an oriented angle about the y-axis
glm::vec3 direction = rotatedSourcePosition * (1.0f / fastSqrtf(rotatedSourcePositionLength2)); glm::vec3 direction = rotatedSourcePosition * (1.0f / fastSqrtf(rotatedSourcePositionLength2));
float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward" float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward"
return (direction.x < 0.0f) ? -angle : angle; return (direction.x < 0.0f) ? -angle : angle;
} else { } else {
@ -2423,4 +2452,4 @@ void AudioClient::setInputVolume(float volume, bool emitSignal) {
emit inputVolumeChanged(_audioInput->volume()); emit inputVolumeChanged(_audioInput->volume());
} }
} }
} }

View file

@ -53,6 +53,7 @@
#include "AudioIOStats.h" #include "AudioIOStats.h"
#include "AudioFileWav.h" #include "AudioFileWav.h"
#include "HifiAudioDeviceInfo.h"
#ifdef _WIN32 #ifdef _WIN32
#pragma warning( push ) #pragma warning( push )
@ -102,8 +103,8 @@ public:
_audio(audio), _unfulfilledReads(0) {} _audio(audio), _unfulfilledReads(0) {}
void start() { open(QIODevice::ReadOnly | QIODevice::Unbuffered); } void start() { open(QIODevice::ReadOnly | QIODevice::Unbuffered); }
qint64 readData(char * data, qint64 maxSize) override; qint64 readData(char* data, qint64 maxSize) override;
qint64 writeData(const char * data, qint64 maxSize) override { return 0; } qint64 writeData(const char* data, qint64 maxSize) override { return 0; }
int getRecentUnfulfilledReads() { int unfulfilledReads = _unfulfilledReads; _unfulfilledReads = 0; return unfulfilledReads; } int getRecentUnfulfilledReads() { int unfulfilledReads = _unfulfilledReads; _unfulfilledReads = 0; return unfulfilledReads; }
private: private:
LocalInjectorsStream& _localInjectorsStream; LocalInjectorsStream& _localInjectorsStream;
@ -111,7 +112,7 @@ public:
AudioClient* _audio; AudioClient* _audio;
int _unfulfilledReads; int _unfulfilledReads;
}; };
void startThread(); void startThread();
void negotiateAudioFormat(); void negotiateAudioFormat();
void selectAudioFormat(const QString& selectedCodecName); void selectAudioFormat(const QString& selectedCodecName);
@ -152,12 +153,12 @@ public:
void setIsPlayingBackRecording(bool isPlayingBackRecording) { _isPlayingBackRecording = isPlayingBackRecording; } void setIsPlayingBackRecording(bool isPlayingBackRecording) { _isPlayingBackRecording = isPlayingBackRecording; }
Q_INVOKABLE void setAvatarBoundingBoxParameters(glm::vec3 corner, glm::vec3 scale); Q_INVOKABLE void setAvatarBoundingBoxParameters(glm::vec3 corner, glm::vec3 scale);
bool outputLocalInjector(const AudioInjectorPointer& injector) override; bool outputLocalInjector(const AudioInjectorPointer& injector) override;
QAudioDeviceInfo getActiveAudioDevice(QAudio::Mode mode) const; HifiAudioDeviceInfo getActiveAudioDevice(QAudio::Mode mode) const;
QList<QAudioDeviceInfo> getAudioDevices(QAudio::Mode mode) const; QList<HifiAudioDeviceInfo> getAudioDevices(QAudio::Mode mode) const;
void enablePeakValues(bool enable) { _enablePeakValues = enable; } void enablePeakValues(bool enable) { _enablePeakValues = enable; }
bool peakValuesAvailable() const; bool peakValuesAvailable() const;
@ -233,11 +234,11 @@ public slots:
int setOutputBufferSize(int numFrames, bool persist = true); int setOutputBufferSize(int numFrames, bool persist = true);
bool shouldLoopbackInjectors() override { return _shouldEchoToServer; } bool shouldLoopbackInjectors() override { return _shouldEchoToServer; }
Q_INVOKABLE void changeDefault(HifiAudioDeviceInfo newDefault, QAudio::Mode mode);
// calling with a null QAudioDevice will use the system default // calling with a null QAudioDevice will use the system default
bool switchAudioDevice(QAudio::Mode mode, const QAudioDeviceInfo& deviceInfo = QAudioDeviceInfo()); bool switchAudioDevice(QAudio::Mode mode, const HifiAudioDeviceInfo& deviceInfo = HifiAudioDeviceInfo());
bool switchAudioDevice(QAudio::Mode mode, const QString& deviceName); bool switchAudioDevice(QAudio::Mode mode, const QString& deviceName);
// Qt opensles plugin is not able to detect when the headset is plugged in // Qt opensles plugin is not able to detect when the headset is plugged in
void setHeadsetPluggedIn(bool pluggedIn); void setHeadsetPluggedIn(bool pluggedIn);
@ -269,10 +270,10 @@ signals:
void noiseGateOpened(); void noiseGateOpened();
void noiseGateClosed(); void noiseGateClosed();
void changeDevice(const QAudioDeviceInfo& outputDeviceInfo); void changeDevice(const HifiAudioDeviceInfo& outputDeviceInfo);
void deviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& device); void deviceChanged(QAudio::Mode mode, const HifiAudioDeviceInfo& device);
void devicesChanged(QAudio::Mode mode, const QList<QAudioDeviceInfo>& devices); void devicesChanged(QAudio::Mode mode, const QList<HifiAudioDeviceInfo>& devices);
void peakValueListChanged(const QList<float> peakValueList); void peakValueListChanged(const QList<float> peakValueList);
void receivedFirstPacket(); void receivedFirstPacket();
@ -416,7 +417,7 @@ private:
float* _localOutputMixBuffer { NULL }; float* _localOutputMixBuffer { NULL };
Mutex _localAudioMutex; Mutex _localAudioMutex;
AudioLimiter _audioLimiter; AudioLimiter _audioLimiter;
// Adds Reverb // Adds Reverb
void configureReverb(); void configureReverb();
void updateReverbOptions(); void updateReverbOptions();
@ -437,8 +438,8 @@ private:
void processWebrtcNearEnd(int16_t* samples, int numFrames, int numChannels, int sampleRate); void processWebrtcNearEnd(int16_t* samples, int numFrames, int numChannels, int sampleRate);
#endif #endif
bool switchInputToAudioDevice(const QAudioDeviceInfo inputDeviceInfo, bool isShutdownRequest = false); bool switchInputToAudioDevice(const HifiAudioDeviceInfo inputDeviceInfo, bool isShutdownRequest = false);
bool switchOutputToAudioDevice(const QAudioDeviceInfo outputDeviceInfo, bool isShutdownRequest = false); bool switchOutputToAudioDevice(const HifiAudioDeviceInfo outputDeviceInfo, bool isShutdownRequest = false);
// Callback acceleration dependent calculations // Callback acceleration dependent calculations
int calculateNumberOfInputCallbackBytes(const QAudioFormat& format) const; int calculateNumberOfInputCallbackBytes(const QAudioFormat& format) const;
@ -459,11 +460,11 @@ private:
glm::vec3 avatarBoundingBoxCorner; glm::vec3 avatarBoundingBoxCorner;
glm::vec3 avatarBoundingBoxScale; glm::vec3 avatarBoundingBoxScale;
QAudioDeviceInfo _inputDeviceInfo; HifiAudioDeviceInfo _inputDeviceInfo;
QAudioDeviceInfo _outputDeviceInfo; HifiAudioDeviceInfo _outputDeviceInfo;
QList<QAudioDeviceInfo> _inputDevices; QList<HifiAudioDeviceInfo> _inputDevices;
QList<QAudioDeviceInfo> _outputDevices; QList<HifiAudioDeviceInfo> _outputDevices;
AudioFileWav _audioFileWav; AudioFileWav _audioFileWav;
@ -476,7 +477,7 @@ private:
CodecPluginPointer _codec; CodecPluginPointer _codec;
QString _selectedCodecName; QString _selectedCodecName;
Encoder* _encoder { nullptr }; // for outbound mic stream Encoder* _encoder { nullptr }; // for outbound mic stream
RateCounter<> _silentOutbound; RateCounter<> _silentOutbound;
RateCounter<> _audioOutbound; RateCounter<> _audioOutbound;
@ -484,11 +485,11 @@ private:
RateCounter<> _audioInbound; RateCounter<> _audioInbound;
#if defined(Q_OS_ANDROID) #if defined(Q_OS_ANDROID)
bool _shouldRestartInputSetup { true }; // Should we restart the input device because of an unintended stop? bool _shouldRestartInputSetup { true }; // Should we restart the input device because of an unintended stop?
#endif #endif
AudioSolo _solo; AudioSolo _solo;
Mutex _checkDevicesMutex; Mutex _checkDevicesMutex;
QTimer* _checkDevicesTimer { nullptr }; QTimer* _checkDevicesTimer { nullptr };
Mutex _checkPeakValuesMutex; Mutex _checkPeakValuesMutex;
@ -498,4 +499,4 @@ private:
}; };
#endif // hifi_AudioClient_h #endif // hifi_AudioClient_h

View file

@ -0,0 +1,36 @@
//
// HifiAudioDeviceInfo.cpp
// libraries/audio-client/src
//
// Created by Amer Cerkic on 9/14/19.
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "HifiAudioDeviceInfo.h"
const QString HifiAudioDeviceInfo::DEFAULT_DEVICE_NAME = "default ";
void HifiAudioDeviceInfo::setDevice(QAudioDeviceInfo devInfo) {
_audioDeviceInfo = devInfo;
}
HifiAudioDeviceInfo& HifiAudioDeviceInfo::operator=(const HifiAudioDeviceInfo& other) {
_audioDeviceInfo = other.getDevice();
_mode = other.getMode();
_isDefault = other.isDefault();
return *this;
}
bool HifiAudioDeviceInfo::operator==(const HifiAudioDeviceInfo& rhs) const {
//Does the QAudioDeviceinfo match as well as is this the default device or
return getDevice() == rhs.getDevice() && isDefault() == rhs.isDefault();
}
bool HifiAudioDeviceInfo::operator!=(const HifiAudioDeviceInfo& rhs) const {
return getDevice() != rhs.getDevice() || isDefault() != rhs.isDefault();
}

View file

@ -0,0 +1,69 @@
//
// HifiAudioDeviceInfo.h
// libraries/audio-client/src
//
// Created by Amer Cerkic on 9/14/19.
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#ifndef hifi_audiodeviceinfo_h
#define hifi_audiodeviceinfo_h
#include <QObject>
#include <QAudioDeviceInfo>
#include <QAudio>
#include <QString>
class HifiAudioDeviceInfo : public QObject {
Q_OBJECT
public:
HifiAudioDeviceInfo() : QObject() {}
HifiAudioDeviceInfo(const HifiAudioDeviceInfo &deviceInfo) : QObject(){
_audioDeviceInfo = deviceInfo.getDevice();
_mode = deviceInfo.getMode();
_isDefault = deviceInfo.isDefault();
}
HifiAudioDeviceInfo(QAudioDeviceInfo deviceInfo, bool isDefault, QAudio::Mode mode) :
_audioDeviceInfo(deviceInfo),
_isDefault(isDefault),
_mode(mode){
}
void setMode(QAudio::Mode mode) { _mode = mode; }
void setIsDefault() { _isDefault = true; }
void setDevice(QAudioDeviceInfo devInfo);
QString deviceName() const {
#if defined(Q_OS_ANDROID)
return _audioDeviceInfo.deviceName();
#endif
if (_isDefault) {
return DEFAULT_DEVICE_NAME;
} else {
return _audioDeviceInfo.deviceName();
}
}
QAudioDeviceInfo getDevice() const { return _audioDeviceInfo; }
bool isDefault() const { return _isDefault; }
QAudio::Mode getMode() const { return _mode; }
HifiAudioDeviceInfo& operator=(const HifiAudioDeviceInfo& other);
bool operator==(const HifiAudioDeviceInfo& rhs) const;
bool operator!=(const HifiAudioDeviceInfo& rhs) const;
private:
QAudioDeviceInfo _audioDeviceInfo;
bool _isDefault { false };
QAudio::Mode _mode { QAudio::AudioInput };
public:
static const QString DEFAULT_DEVICE_NAME;
};
#endif

View file

@ -356,7 +356,7 @@ void OffscreenQmlSurface::onRootCreated() {
getSurfaceContext()->setContextProperty("offscreenWindow", QVariant::fromValue(getWindow())); getSurfaceContext()->setContextProperty("offscreenWindow", QVariant::fromValue(getWindow()));
// Connect with the audio client and listen for audio device changes // Connect with the audio client and listen for audio device changes
connect(DependencyManager::get<AudioClient>().data(), &AudioClient::deviceChanged, this, [this](QAudio::Mode mode, const QAudioDeviceInfo& device) { connect(DependencyManager::get<AudioClient>().data(), &AudioClient::deviceChanged, this, [this](QAudio::Mode mode, const HifiAudioDeviceInfo& device) {
if (mode == QAudio::Mode::AudioOutput) { if (mode == QAudio::Mode::AudioOutput) {
QMetaObject::invokeMethod(this, "changeAudioOutputDevice", Qt::QueuedConnection, Q_ARG(QString, device.deviceName())); QMetaObject::invokeMethod(this, "changeAudioOutputDevice", Qt::QueuedConnection, Q_ARG(QString, device.deviceName()));
} }