diff --git a/examples/lobby.js b/examples/lobby.js index c921e744f1..eb2611fd29 100644 --- a/examples/lobby.js +++ b/examples/lobby.js @@ -38,7 +38,8 @@ var panelsCenterShift = Vec3.subtract(panelsCenter, orbCenter); var ORB_SHIFT = { x: 0, y: -1.4, z: -0.8}; var HELMET_ATTACHMENT_URL = HIFI_PUBLIC_BUCKET + "models/attachments/IronManMaskOnly.fbx" -var droneSound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/drone.raw") + +var droneSound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/latin.raw") var currentDrone; function reticlePosition() { diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 08a29a08ff..a5f6bd897f 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -1336,21 +1336,25 @@ void Audio::startDrumSound(float volume, float frequency, float duration, float _drumSoundSample = 0; } -QIODevice* Audio::newLocalOutputDevice(bool isStereo, qreal volume, int numBytes, AudioInjector* injector) { - QAudioFormat localFormat = _desiredOutputFormat; - localFormat.setChannelCount(isStereo ? 2 : 1); +bool Audio::outputLocalInjector(bool isStereo, qreal volume, AudioInjector* injector) { + if (injector->getLocalBuffer()) { + QAudioFormat localFormat = _desiredOutputFormat; + localFormat.setChannelCount(isStereo ? 2 : 1); + + QAudioOutput* localOutput = new QAudioOutput(getNamedAudioDeviceForMode(QAudio::AudioOutput, _outputAudioDeviceName), + localFormat, this); + localOutput->setVolume(volume); + + // add this to our list of local injected outputs, we will need to clean it up when the injector says it is done + _injectedOutputInterfaces.insert(injector, localOutput); + + connect(injector, &AudioInjector::finished, this, &Audio::cleanupLocalOutputInterface); + + localOutput->start(injector->getLocalBuffer()); + return localOutput->state() == QAudio::ActiveState; + } - QAudioOutput* localOutput = new QAudioOutput(getNamedAudioDeviceForMode(QAudio::AudioOutput, _outputAudioDeviceName), - localFormat, this); - localOutput->setBufferSize(numBytes); - localOutput->setVolume(volume); - - // add this to our list of local injected outputs, we will need to clean it up when the injector says it is done - _injectedOutputInterfaces.insert(injector, localOutput); - - connect(injector, &AudioInjector::finished, this, &Audio::cleanupLocalOutputInterface); - - return localOutput->start(); + return false; } void Audio::cleanupLocalOutputInterface() { diff --git a/interface/src/Audio.h b/interface/src/Audio.h index cc404723d8..d6e26864e6 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -155,7 +155,7 @@ public slots: void selectAudioFilterBassCut(); void selectAudioFilterSmiley(); - virtual QIODevice* newLocalOutputDevice(bool isStereo, qreal volume, int numBytes, AudioInjector* injector); + virtual bool outputLocalInjector(bool isStereo, qreal volume, AudioInjector* injector); void sendDownstreamAudioStatsPacket(); diff --git a/libraries/audio/src/AbstractAudioInterface.h b/libraries/audio/src/AbstractAudioInterface.h index 104abae554..b5a7be9849 100644 --- a/libraries/audio/src/AbstractAudioInterface.h +++ b/libraries/audio/src/AbstractAudioInterface.h @@ -18,6 +18,7 @@ #include "AudioInjectorOptions.h" class AudioInjector; +class AudioInjectorLocalBuffer; class AbstractAudioInterface : public QObject { Q_OBJECT @@ -27,7 +28,7 @@ public: virtual void startCollisionSound(float magnitude, float frequency, float noise, float duration, bool flashScreen) = 0; virtual void startDrumSound(float volume, float frequency, float duration, float decay) = 0; public slots: - virtual QIODevice* newLocalOutputDevice(bool isStereo, qreal volume, int numBytes, AudioInjector* injector) = 0; + virtual bool outputLocalInjector(bool isStereo, qreal volume, AudioInjector* injector) = 0; }; Q_DECLARE_METATYPE(AbstractAudioInterface*) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index a0914ca741..ddd4f05630 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -37,7 +37,7 @@ AudioInjector::AudioInjector(QObject* parent) : _loudness(0.0f), _isFinished(false), _currentSendPosition(0), - _localDevice(NULL) + _localBuffer(NULL) { } @@ -47,10 +47,17 @@ AudioInjector::AudioInjector(Sound* sound, const AudioInjectorOptions& injectorO _shouldStop(false), _loudness(0.0f), _isFinished(false), - _currentSendPosition(0) + _currentSendPosition(0), + _localBuffer(NULL) { } +AudioInjector::~AudioInjector() { + if (_localBuffer) { + _localBuffer->stop(); + } +} + void AudioInjector::setOptions(AudioInjectorOptions& options) { _options = options; } @@ -72,19 +79,25 @@ void AudioInjector::injectLocally() { const QByteArray& soundByteArray = _sound->getByteArray(); if (soundByteArray.size() > 0) { - QMetaObject::invokeMethod(_localAudioInterface, "newLocalOutputDevice", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QIODevice*, _localDevice), + bool successfulOutput = false; + + _localBuffer = new AudioInjectorLocalBuffer(_sound->getByteArray(), this); + _localBuffer->open(QIODevice::ReadOnly); + _localBuffer->setIsLooping(_options.loop); + + qDebug() << "Passing off AudioInjectorLocatBuffer to localAudioInterface"; + + QMetaObject::invokeMethod(_localAudioInterface, "outputLocalInjector", + Qt::BlockingQueuedConnection, + Q_RETURN_ARG(bool, successfulOutput), Q_ARG(bool, _options.stereo), Q_ARG(qreal, _options.volume), - Q_ARG(int, soundByteArray.size()), Q_ARG(AudioInjector*, this)); - if (_localDevice) { - // immediately write the byte array to the local device - qDebug() << "Writing" << soundByteArray.size() << "bytes to local audio device"; - _localDevice->write(soundByteArray); - } else { - qDebug() << "AudioInjector::injectLocally did not get a valid QIODevice from _localAudioInterface"; + + + if (!successfulOutput) { + qDebug() << "AudioInjector::injectLocally could not output locally via _localAudioInterface"; } } else { qDebug() << "AudioInjector::injectLocally called without any data in Sound QByteArray"; @@ -224,8 +237,11 @@ void AudioInjector::injectToMixer() { void AudioInjector::stop() { _shouldStop = true; - if (_localDevice) { - // we're only a local injector, so we can say we are finished and the AbstractAudioInterface should clean us up + if (_localBuffer) { + // we're only a local injector, so we can say we are finished right away too + + _localBuffer->stop(); + _isFinished = true; emit finished(); } diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 98d391e494..607aeb500e 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -18,6 +18,7 @@ #include #include +#include "AudioInjectorLocalBuffer.h" #include "AudioInjectorOptions.h" #include "Sound.h" @@ -28,10 +29,14 @@ class AudioInjector : public QObject { public: AudioInjector(QObject* parent); AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions); + ~AudioInjector(); bool isFinished() const { return _isFinished; } int getCurrentSendPosition() const { return _currentSendPosition; } + AudioInjectorLocalBuffer* getLocalBuffer() const { return _localBuffer; } + bool isLocalOnly() const { return _options.localOnly; } + void setLocalAudioInterface(AbstractAudioInterface* localAudioInterface) { _localAudioInterface = localAudioInterface; } public slots: void injectAudio(); @@ -53,7 +58,7 @@ private: bool _isFinished; int _currentSendPosition; AbstractAudioInterface* _localAudioInterface; - QIODevice* _localDevice; + AudioInjectorLocalBuffer* _localBuffer; }; Q_DECLARE_METATYPE(AudioInjector*) diff --git a/libraries/audio/src/AudioInjectorLocalBuffer.cpp b/libraries/audio/src/AudioInjectorLocalBuffer.cpp new file mode 100644 index 0000000000..19c92276f9 --- /dev/null +++ b/libraries/audio/src/AudioInjectorLocalBuffer.cpp @@ -0,0 +1,43 @@ +// +// AudioInjectorLocalBuffer.cpp +// libraries/audio/src +// +// Created by Stephen Birarda on 2014-11-11. +// Copyright 2014 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 "AudioInjectorLocalBuffer.h" + +AudioInjectorLocalBuffer::AudioInjectorLocalBuffer(const QByteArray& rawAudioArray, QObject* parent) : + QIODevice(parent), + _rawAudioArray(rawAudioArray), + _isLooping(false), + _isStopped(false) +{ + +} + +void AudioInjectorLocalBuffer::stop() { + _isStopped = true; + QIODevice::close(); +} + +qint64 AudioInjectorLocalBuffer::readData(char* data, qint64 maxSize) { + if (!_isStopped) { + int bytesToEnd = _rawAudioArray.size() - pos(); + + int bytesToRead = maxSize; + + if (maxSize > bytesToEnd) { + bytesToRead = bytesToEnd; + } + + memcpy(data, _rawAudioArray.data() + pos(), bytesToRead); + return bytesToRead; + } else { + return 0; + } +} \ No newline at end of file diff --git a/libraries/audio/src/AudioInjectorLocalBuffer.h b/libraries/audio/src/AudioInjectorLocalBuffer.h new file mode 100644 index 0000000000..6d234066ef --- /dev/null +++ b/libraries/audio/src/AudioInjectorLocalBuffer.h @@ -0,0 +1,35 @@ +// +// AudioInjectorLocalBuffer.h +// libraries/audio/src +// +// Created by Stephen Birarda on 2014-11-11. +// Copyright 2014 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 +// + +#ifndef hifi_AudioInjectorLocalBuffer_h +#define hifi_AudioInjectorLocalBuffer_h + +#include + +class AudioInjectorLocalBuffer : public QIODevice { + Q_OBJECT +public: + AudioInjectorLocalBuffer(const QByteArray& rawAudioArray, QObject* parent); + + void stop(); + + qint64 readData(char* data, qint64 maxSize); + qint64 writeData(const char* data, qint64 maxSize) { return 0; } + + void setIsLooping(bool isLooping) { _isLooping = isLooping; } + +private: + QByteArray _rawAudioArray; + bool _isLooping; + bool _isStopped; +}; + +#endif // hifi_AudioInjectorLocalBuffer_h \ No newline at end of file