add support for AudioDevice scripting interface, and switching audio input/output device on the fly

This commit is contained in:
ZappoMan 2014-03-23 11:26:27 -07:00
parent 938959a9f6
commit 82d620b1d9
5 changed files with 190 additions and 23 deletions

View file

@ -72,6 +72,7 @@
#include "devices/TV3DManager.h" #include "devices/TV3DManager.h"
#include "renderer/ProgramObject.h" #include "renderer/ProgramObject.h"
#include "scripting/AudioDeviceScriptingInterface.h"
#include "scripting/ClipboardScriptingInterface.h" #include "scripting/ClipboardScriptingInterface.h"
#include "scripting/MenuScriptingInterface.h" #include "scripting/MenuScriptingInterface.h"
#include "scripting/SettingsScriptingInterface.h" #include "scripting/SettingsScriptingInterface.h"
@ -3552,6 +3553,7 @@ void Application::loadScript(const QString& fileNameString) {
scriptEngine->registerGlobalObject("Overlays", &_overlays); scriptEngine->registerGlobalObject("Overlays", &_overlays);
scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance());
scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance());
scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance());
QThread* workerThread = new QThread(this); QThread* workerThread = new QThread(this);

View file

@ -92,6 +92,16 @@ void Audio::reset() {
_ringBuffer.reset(); _ringBuffer.reset();
} }
QAudioDeviceInfo getNamedAudioDeviceForMode(QAudio::Mode mode, const QString& deviceName) {
QAudioDeviceInfo result;
foreach(QAudioDeviceInfo audioDevice, QAudioDeviceInfo::availableDevices(mode)) {
if (audioDevice.deviceName().trimmed() == deviceName.trimmed()) {
result = audioDevice;
}
}
return result;
}
QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
#ifdef __APPLE__ #ifdef __APPLE__
if (QAudioDeviceInfo::availableDevices(mode).size() > 1) { if (QAudioDeviceInfo::availableDevices(mode).size() > 1) {
@ -249,27 +259,92 @@ void Audio::start() {
_desiredOutputFormat.setChannelCount(2); _desiredOutputFormat.setChannelCount(2);
QAudioDeviceInfo inputDeviceInfo = defaultAudioDeviceForMode(QAudio::AudioInput); QAudioDeviceInfo inputDeviceInfo = defaultAudioDeviceForMode(QAudio::AudioInput);
qDebug() << "The audio input device is" << inputDeviceInfo.deviceName(); qDebug() << "The default audio input device is" << inputDeviceInfo.deviceName();
bool inputFormatSupported = switchInputToAudioDevice(inputDeviceInfo.deviceName());
QAudioDeviceInfo outputDeviceInfo = defaultAudioDeviceForMode(QAudio::AudioOutput);
qDebug() << "The default audio output device is" << outputDeviceInfo.deviceName();
bool outputFormatSupported = switchOutputToAudioDevice(outputDeviceInfo.deviceName());
if (adjustedFormatForAudioDevice(inputDeviceInfo, _desiredInputFormat, _inputFormat)) { if (!inputFormatSupported || !outputFormatSupported) {
qDebug() << "The format to be used for audio input is" << _inputFormat; qDebug() << "Unable to set up audio I/O because of a problem with input or output formats.";
}
}
bool Audio::switchInputToAudioDevice(const QString& inputDeviceName) {
bool supportedFormat = false;
// cleanup any previously initialized device
if (_audioInput) {
_audioInput->stop();
disconnect(_inputDevice, 0, 0, 0);
_inputDevice = NULL;
delete _audioInput;
_audioInput = NULL;
_numInputCallbackBytes = 0;
_inputAudioDeviceName = "";
}
QAudioDeviceInfo inputDeviceInfo = getNamedAudioDeviceForMode(QAudio::AudioInput, inputDeviceName);
if (!inputDeviceInfo.isNull()) {
qDebug() << "The audio input device " << inputDeviceInfo.deviceName() << "is available.";
_inputAudioDeviceName = inputDeviceInfo.deviceName().trimmed();
if (adjustedFormatForAudioDevice(inputDeviceInfo, _desiredInputFormat, _inputFormat)) {
qDebug() << "The format to be used for audio input is" << _inputFormat;
_audioInput = new QAudioInput(inputDeviceInfo, _inputFormat, this); _audioInput = new QAudioInput(inputDeviceInfo, _inputFormat, this);
_numInputCallbackBytes = NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL * _inputFormat.channelCount() _numInputCallbackBytes = NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL * _inputFormat.channelCount()
* (_inputFormat.sampleRate() / SAMPLE_RATE) * (_inputFormat.sampleRate() / SAMPLE_RATE)
/ CALLBACK_ACCELERATOR_RATIO; / CALLBACK_ACCELERATOR_RATIO;
_audioInput->setBufferSize(_numInputCallbackBytes); _audioInput->setBufferSize(_numInputCallbackBytes);
QAudioDeviceInfo outputDeviceInfo = defaultAudioDeviceForMode(QAudio::AudioOutput); // how do we want to handle input working, but output not working?
qDebug() << "The audio output device is" << outputDeviceInfo.deviceName();
if (adjustedFormatForAudioDevice(outputDeviceInfo, _desiredOutputFormat, _outputFormat)) {
qDebug() << "The format to be used for audio output is" << _outputFormat;
_inputRingBuffer.resizeForFrameSize(_numInputCallbackBytes * CALLBACK_ACCELERATOR_RATIO / sizeof(int16_t)); _inputRingBuffer.resizeForFrameSize(_numInputCallbackBytes * CALLBACK_ACCELERATOR_RATIO / sizeof(int16_t));
_inputDevice = _audioInput->start(); _inputDevice = _audioInput->start();
connect(_inputDevice, SIGNAL(readyRead()), this, SLOT(handleAudioInput())); connect(_inputDevice, SIGNAL(readyRead()), this, SLOT(handleAudioInput()));
supportedFormat = true;
}
}
return supportedFormat;
}
bool Audio::switchOutputToAudioDevice(const QString& outputDeviceName) {
bool supportedFormat = false;
// cleanup any previously initialized device
if (_audioOutput) {
_audioOutput->stop();
disconnect(_outputDevice, 0, 0, 0);
_outputDevice = NULL;
delete _audioOutput;
_audioOutput = NULL;
_numInputCallbackBytes = 0;
_loopbackOutputDevice = NULL;
delete _loopbackAudioOutput;
_loopbackAudioOutput = NULL;
_proceduralOutputDevice = NULL;
delete _proceduralAudioOutput;
_proceduralAudioOutput = NULL;
_outputAudioDeviceName = "";
}
QAudioDeviceInfo outputDeviceInfo = getNamedAudioDeviceForMode(QAudio::AudioOutput, outputDeviceName);
if (!outputDeviceInfo.isNull()) {
qDebug() << "The audio output device " << outputDeviceInfo.deviceName() << "is available.";
_outputAudioDeviceName = outputDeviceInfo.deviceName().trimmed();
if (adjustedFormatForAudioDevice(outputDeviceInfo, _desiredOutputFormat, _outputFormat)) {
qDebug() << "The format to be used for audio output is" << _outputFormat;
// 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, _outputFormat, this);
_audioOutput->setBufferSize(_ringBuffer.getSampleCapacity() * sizeof(int16_t)); _audioOutput->setBufferSize(_ringBuffer.getSampleCapacity() * sizeof(int16_t));
@ -278,17 +353,15 @@ void Audio::start() {
// setup a loopback audio output device // setup a loopback audio output device
_loopbackAudioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this); _loopbackAudioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this);
// setup a procedural audio output device // setup a procedural audio output device
_proceduralAudioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this); _proceduralAudioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this);
gettimeofday(&_lastReceiveTime, NULL); gettimeofday(&_lastReceiveTime, NULL);
supportedFormat = true;
} }
return;
} }
return supportedFormat;
qDebug() << "Unable to set up audio I/O because of a problem with input or output formats.";
} }
void Audio::handleAudioInput() { void Audio::handleAudioInput() {
@ -315,7 +388,9 @@ void Audio::handleAudioInput() {
} }
if (_inputFormat == _outputFormat) { if (_inputFormat == _outputFormat) {
_loopbackOutputDevice->write(inputByteArray); if (_loopbackOutputDevice) {
_loopbackOutputDevice->write(inputByteArray);
}
} else { } else {
static float loopbackOutputToInputRatio = (_outputFormat.sampleRate() / (float) _inputFormat.sampleRate()) static float loopbackOutputToInputRatio = (_outputFormat.sampleRate() / (float) _inputFormat.sampleRate())
* (_outputFormat.channelCount() / _inputFormat.channelCount()); * (_outputFormat.channelCount() / _inputFormat.channelCount());
@ -326,7 +401,9 @@ void Audio::handleAudioInput() {
inputByteArray.size() / sizeof(int16_t), inputByteArray.size() / sizeof(int16_t),
loopBackByteArray.size() / sizeof(int16_t), _inputFormat, _outputFormat); loopBackByteArray.size() / sizeof(int16_t), _inputFormat, _outputFormat);
_loopbackOutputDevice->write(loopBackByteArray); if (_loopbackOutputDevice) {
_loopbackOutputDevice->write(loopBackByteArray);
}
} }
} }
@ -469,7 +546,9 @@ void Audio::handleAudioInput() {
NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * 4, NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * 4,
_desiredInputFormat, _outputFormat); _desiredInputFormat, _outputFormat);
_proceduralOutputDevice->write(proceduralOutput); if (_proceduralOutputDevice) {
_proceduralOutputDevice->write(proceduralOutput);
}
NodeList* nodeList = NodeList::getInstance(); NodeList* nodeList = NodeList::getInstance();
SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer);

View file

@ -72,7 +72,7 @@ public:
int getNetworkSampleRate() { return SAMPLE_RATE; } int getNetworkSampleRate() { return SAMPLE_RATE; }
int getNetworkBufferLengthSamplesPerChannel() { return NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; } int getNetworkBufferLengthSamplesPerChannel() { return NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; }
public slots: public slots:
void start(); void start();
void addReceivedAudioToBuffer(const QByteArray& audioByteArray); void addReceivedAudioToBuffer(const QByteArray& audioByteArray);
@ -83,10 +83,18 @@ public slots:
virtual void handleAudioByteArray(const QByteArray& audioByteArray); virtual void handleAudioByteArray(const QByteArray& audioByteArray);
bool switchInputToAudioDevice(const QString& inputDeviceName);
bool switchOutputToAudioDevice(const QString& outputDeviceName);
QString getInputAudioDeviceName() const { return _inputAudioDeviceName; }
QString getOutputAudioDeviceName() const { return _outputAudioDeviceName; }
signals: signals:
bool muteToggled(); bool muteToggled();
private: private:
QByteArray firstInputFrame; QByteArray firstInputFrame;
QAudioInput* _audioInput; QAudioInput* _audioInput;
QAudioFormat _desiredInputFormat; QAudioFormat _desiredInputFormat;
@ -105,6 +113,9 @@ private:
QIODevice* _proceduralOutputDevice; QIODevice* _proceduralOutputDevice;
AudioRingBuffer _inputRingBuffer; AudioRingBuffer _inputRingBuffer;
AudioRingBuffer _ringBuffer; AudioRingBuffer _ringBuffer;
QString _inputAudioDeviceName;
QString _outputAudioDeviceName;
Oscilloscope* _scope; Oscilloscope* _scope;
StDev _stdev; StDev _stdev;

View file

@ -0,0 +1,44 @@
//
// AudioDeviceScriptingInterface.cpp
// hifi
//
// Created by Brad Hefta-Gaub on 3/23/14
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//
#include "Application.h"
#include "AudioDeviceScriptingInterface.h"
AudioDeviceScriptingInterface* AudioDeviceScriptingInterface::getInstance() {
static AudioDeviceScriptingInterface sharedInstance;
return &sharedInstance;
}
bool AudioDeviceScriptingInterface::setInputDevice(const QString& deviceName) {
bool result;
QMetaObject::invokeMethod(Application::getInstance()->getAudio(), "switchInputToAudioDevice",
Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, result),
Q_ARG(const QString&, deviceName));
return result;
}
bool AudioDeviceScriptingInterface::setOutputDevice(const QString& deviceName) {
bool result;
QMetaObject::invokeMethod(Application::getInstance()->getAudio(), "switchOutputToAudioDevice",
Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, result),
Q_ARG(const QString&, deviceName));
return result;
}
QString AudioDeviceScriptingInterface::getInputDevice() {
return Application::getInstance()->getAudio()->getInputAudioDeviceName();
}
QString AudioDeviceScriptingInterface::getOutputDevice() {
return Application::getInstance()->getAudio()->getOutputAudioDeviceName();
}

View file

@ -0,0 +1,31 @@
//
// AudioDeviceScriptingInterface.h
// hifi
//
// Created by Brad Hefta-Gaub on 3/22/14
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//
#ifndef __hifi__AudioDeviceScriptingInterface__
#define __hifi__AudioDeviceScriptingInterface__
#include <QDebug>
#include <QObject>
#include <QString>
#include "Application.h"
class AudioDeviceScriptingInterface : public QObject {
Q_OBJECT
AudioDeviceScriptingInterface() { };
public:
static AudioDeviceScriptingInterface* getInstance();
public slots:
bool setInputDevice(const QString& deviceName);
bool setOutputDevice(const QString& deviceName);
QString getInputDevice();
QString getOutputDevice();
};
#endif /* defined(__hifi__AudioDeviceScriptingInterface__) */