diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index fa171f252d..b876e5290d 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -94,7 +94,8 @@ void AudioMixerClientData::pushBuffersAfterFrameSend() { audioBuffer->shiftReadPosition(NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL); audioBuffer->setWillBeAddedToMix(false); - } else if (audioBuffer->hasStarted() && audioBuffer->isStarved()) { + } else if (audioBuffer->getType() == PositionalAudioRingBuffer::Injector + && audioBuffer->hasStarted() && audioBuffer->isStarved()) { // this is an empty audio buffer that has starved, safe to delete delete audioBuffer; _ringBuffers.erase(_ringBuffers.begin() + i); diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 6b776f5cfa..9b75b78b46 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -127,11 +127,10 @@ void Hand::simulateToyBall(PalmData& palm, const glm::vec3& fingerTipPosition, f // use the threadSound static method to inject the catch sound // pass an AudioInjectorOptions struct to set position and disable loopback AudioInjectorOptions injectorOptions; - injectorOptions.position = targetPosition; - injectorOptions.shouldLoopback = false; - injectorOptions.loopbackAudioInterface = app->getAudio(); - - AudioInjector::threadSound(&_catchSound, injectorOptions); + injectorOptions.setPosition(newPosition); + injectorOptions.setLoopbackAudioInterface(app->getAudio()); + + AudioScriptingInterface::playSound(&_catchSound, &injectorOptions); } } @@ -216,11 +215,10 @@ void Hand::simulateToyBall(PalmData& palm, const glm::vec3& fingerTipPosition, f // use the threadSound static method to inject the throw sound // pass an AudioInjectorOptions struct to set position and disable loopback AudioInjectorOptions injectorOptions; - injectorOptions.position = targetPosition; - injectorOptions.shouldLoopback = false; - injectorOptions.loopbackAudioInterface = app->getAudio(); + injectorOptions.setPosition(ballPosition); + injectorOptions.setLoopbackAudioInterface(app->getAudio()); - AudioInjector::threadSound(&_throwSound, injectorOptions); + AudioScriptingInterface::playSound(&_throwSound, &injectorOptions); } } diff --git a/interface/src/avatar/Hand.h b/interface/src/avatar/Hand.h index 88ca4a61e7..60d7dd26ac 100755 --- a/interface/src/avatar/Hand.h +++ b/interface/src/avatar/Hand.h @@ -17,7 +17,7 @@ #include #include -#include +#include #include #include diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 5ad4dc6f4d..c7ca388139 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -3,7 +3,7 @@ // hifi // // Created by Stephen Birarda on 1/2/2014. -// Copyright (c) 2013 HighFidelity, Inc. All rights reserved. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. // #include @@ -18,33 +18,11 @@ int abstractAudioPointerMeta = qRegisterMetaType("AbstractAudioInterface*"); -AudioInjector::AudioInjector(Sound* sound, AudioInjectorOptions injectorOptions) : - _thread(NULL), +AudioInjector::AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions) : _sound(sound), - _volume(injectorOptions.volume), - _shouldLoopback(injectorOptions.shouldLoopback), - _position(injectorOptions.position), - _orientation(injectorOptions.orientation), - _loopbackAudioInterface(injectorOptions.loopbackAudioInterface) + _options(injectorOptions) { - _thread = new QThread(); - // we want to live on our own thread - moveToThread(_thread); -} - -void AudioInjector::threadSound(Sound* sound, AudioInjectorOptions injectorOptions) { - AudioInjector* injector = new AudioInjector(sound, injectorOptions); - - // start injecting when the injector thread starts - connect(injector->_thread, SIGNAL(started()), injector, SLOT(injectAudio())); - - // connect the right slots and signals so that the AudioInjector is killed once the injection is complete - connect(injector, SIGNAL(finished()), injector, SLOT(deleteLater())); - connect(injector, SIGNAL(finished()), injector->_thread, SLOT(quit())); - connect(injector->_thread, SIGNAL(finished()), injector->_thread, SLOT(deleteLater())); - - injector->_thread->start(); } const uchar MAX_INJECTOR_VOLUME = 0xFF; @@ -56,9 +34,9 @@ void AudioInjector::injectAudio() { // make sure we actually have samples downloaded to inject if (soundByteArray.size()) { // give our sample byte array to the local audio interface, if we have it, so it can be handled locally - if (_loopbackAudioInterface) { + if (_options.getLoopbackAudioInterface()) { // assume that localAudioInterface could be on a separate thread, use Qt::AutoConnection to handle properly - QMetaObject::invokeMethod(_loopbackAudioInterface, "handleAudioByteArray", + QMetaObject::invokeMethod(_options.getLoopbackAudioInterface(), "handleAudioByteArray", Qt::AutoConnection, Q_ARG(QByteArray, soundByteArray)); @@ -85,16 +63,17 @@ void AudioInjector::injectAudio() { currentPacketPosition += rfcStreamUUID.size(); // pack the flag for loopback - memcpy(currentPacketPosition, &_shouldLoopback, sizeof(_shouldLoopback)); - currentPacketPosition += sizeof(_shouldLoopback); + bool loopbackFlag = (_options.getLoopbackAudioInterface() == NULL); + memcpy(currentPacketPosition, &loopbackFlag, sizeof(loopbackFlag)); + currentPacketPosition += sizeof(loopbackFlag); // pack the position for injected audio - memcpy(currentPacketPosition, &_position, sizeof(_position)); - currentPacketPosition += sizeof(_position); + memcpy(currentPacketPosition, &_options.getPosition(), sizeof(_options.getPosition())); + currentPacketPosition += sizeof(_options.getPosition()); // pack our orientation for injected audio - memcpy(currentPacketPosition, &_orientation, sizeof(_orientation)); - currentPacketPosition += sizeof(_orientation); + memcpy(currentPacketPosition, &_options.getOrientation(), sizeof(_options.getOrientation())); + currentPacketPosition += sizeof(_options.getOrientation()); // pack zero for radius float radius = 0; @@ -102,7 +81,7 @@ void AudioInjector::injectAudio() { currentPacketPosition += sizeof(radius); // pack 255 for attenuation byte - uchar volume = MAX_INJECTOR_VOLUME * _volume; + uchar volume = MAX_INJECTOR_VOLUME * _options.getVolume(); memcpy(currentPacketPosition, &volume, sizeof(volume)); currentPacketPosition += sizeof(volume); diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index f2e7b3b498..1e09a2c3c2 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -3,7 +3,7 @@ // hifi // // Created by Stephen Birarda on 1/2/2014. -// Copyright (c) 2013 HighFidelity, Inc. All rights reserved. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. // #ifndef __hifi__AudioInjector__ @@ -15,39 +15,20 @@ #include #include +#include "AudioInjectorOptions.h" #include "Sound.h" class AbstractAudioInterface; - -struct AudioInjectorOptions { - AudioInjectorOptions() : position(glm::vec3(0.0f, 0.0f, 0.0f)), - volume(1.0f), - orientation(glm::quat()), - shouldLoopback(true), - loopbackAudioInterface(NULL) {}; - - glm::vec3 position; - float volume; - const glm::quat orientation; - bool shouldLoopback; - AbstractAudioInterface* loopbackAudioInterface; -}; +class AudioScriptingInterface; class AudioInjector : public QObject { Q_OBJECT public: - static void threadSound(Sound* sound, AudioInjectorOptions injectorOptions = AudioInjectorOptions()); + AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions); private: - AudioInjector(Sound* sound, AudioInjectorOptions injectorOptions); - - QThread* _thread; Sound* _sound; - float _volume; - uchar _shouldLoopback; - glm::vec3 _position; - glm::quat _orientation; - AbstractAudioInterface* _loopbackAudioInterface; -private slots: + AudioInjectorOptions _options; +public slots: void injectAudio(); signals: void finished(); diff --git a/libraries/audio/src/AudioInjectorOptions.cpp b/libraries/audio/src/AudioInjectorOptions.cpp new file mode 100644 index 0000000000..30e8c3e490 --- /dev/null +++ b/libraries/audio/src/AudioInjectorOptions.cpp @@ -0,0 +1,26 @@ +// +// AudioInjectorOptions.cpp +// hifi +// +// Created by Stephen Birarda on 1/2/2014. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// + +#include "AudioInjectorOptions.h" + +AudioInjectorOptions::AudioInjectorOptions(QObject* parent) : + QObject(parent), + _position(0.0f, 0.0f, 0.0f), + _volume(1.0f), + _orientation(glm::vec3(0.0f, 0.0f, 0.0f)), + _loopbackAudioInterface(NULL) +{ + +} + +AudioInjectorOptions::AudioInjectorOptions(const AudioInjectorOptions& other) { + _position = other._position; + _volume = other._volume; + _orientation = other._orientation; + _loopbackAudioInterface = other._loopbackAudioInterface; +} \ No newline at end of file diff --git a/libraries/audio/src/AudioInjectorOptions.h b/libraries/audio/src/AudioInjectorOptions.h new file mode 100644 index 0000000000..7da0c9f2a0 --- /dev/null +++ b/libraries/audio/src/AudioInjectorOptions.h @@ -0,0 +1,48 @@ +// +// AudioInjectorOptions.h +// hifi +// +// Created by Stephen Birarda on 1/2/2014. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// + +#ifndef __hifi__AudioInjectorOptions__ +#define __hifi__AudioInjectorOptions__ + +#include + +#include +#include + +#include + +#include "AbstractAudioInterface.h" + +class AudioInjectorOptions : public QObject { + Q_OBJECT + + Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition) +public: + AudioInjectorOptions(QObject* parent = 0); + AudioInjectorOptions(const AudioInjectorOptions& other); + + const glm::vec3& getPosition() const { return _position; } + void setPosition(const glm::vec3& position) { _position = position; } + + float getVolume() const { return _volume; } + void setVolume(float volume) { _volume = volume; } + + const glm::quat& getOrientation() const { return _orientation; } + void setOrientation(const glm::quat& orientation) { _orientation = orientation; } + + AbstractAudioInterface* getLoopbackAudioInterface() const { return _loopbackAudioInterface; } + void setLoopbackAudioInterface(AbstractAudioInterface* loopbackAudioInterface) + { _loopbackAudioInterface = loopbackAudioInterface; } +private: + glm::vec3 _position; + float _volume; + glm::quat _orientation; + AbstractAudioInterface* _loopbackAudioInterface; +}; + +#endif /* defined(__hifi__AudioInjectorOptions__) */ diff --git a/libraries/audio/src/AudioRingBuffer.cpp b/libraries/audio/src/AudioRingBuffer.cpp index 4c9b6ff3ab..50bf554eca 100644 --- a/libraries/audio/src/AudioRingBuffer.cpp +++ b/libraries/audio/src/AudioRingBuffer.cpp @@ -107,8 +107,6 @@ qint64 AudioRingBuffer::writeData(const char* data, qint64 maxSize) { _isStarved = true; } - _hasStarted = true; - if (_endOfLastWrite + samplesToCopy <= _buffer + _sampleCapacity) { memcpy(_endOfLastWrite, data, samplesToCopy * sizeof(int16_t)); } else { diff --git a/libraries/audio/src/AudioScriptingInterface.cpp b/libraries/audio/src/AudioScriptingInterface.cpp new file mode 100644 index 0000000000..d09a4eef86 --- /dev/null +++ b/libraries/audio/src/AudioScriptingInterface.cpp @@ -0,0 +1,28 @@ +// +// AudioScriptingInterface.cpp +// hifi +// +// Created by Stephen Birarda on 1/2/2014. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// + +#include "AudioScriptingInterface.h" + +void AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions* injectorOptions) { + + AudioInjector* injector = new AudioInjector(sound, *injectorOptions); + + QThread* injectorThread = new QThread(); + + injector->moveToThread(injectorThread); + + // start injecting when the injector thread starts + connect(injectorThread, SIGNAL(started()), injector, SLOT(injectAudio())); + + // connect the right slots and signals so that the AudioInjector is killed once the injection is complete + connect(injector, SIGNAL(finished()), injector, SLOT(deleteLater())); + connect(injector, SIGNAL(finished()), injectorThread, SLOT(quit())); + connect(injectorThread, SIGNAL(finished()), injectorThread, SLOT(deleteLater())); + + injectorThread->start(); +} \ No newline at end of file diff --git a/libraries/audio/src/AudioScriptingInterface.h b/libraries/audio/src/AudioScriptingInterface.h new file mode 100644 index 0000000000..bded426cbd --- /dev/null +++ b/libraries/audio/src/AudioScriptingInterface.h @@ -0,0 +1,23 @@ +// +// AudioScriptingInterface.h +// hifi +// +// Created by Stephen Birarda on 1/2/2014. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// + +#ifndef __hifi__AudioScriptingInterface__ +#define __hifi__AudioScriptingInterface__ + +#include "AudioInjector.h" +#include "Sound.h" + +const AudioInjectorOptions DEFAULT_INJECTOR_OPTIONS; + +class AudioScriptingInterface : public QObject { + Q_OBJECT +public slots: + static void playSound(Sound* sound, const AudioInjectorOptions* injectorOptions = NULL); + +}; +#endif /* defined(__hifi__AudioScriptingInterface__) */ diff --git a/libraries/audio/src/InjectedAudioRingBuffer.cpp b/libraries/audio/src/InjectedAudioRingBuffer.cpp index 3d4d8a1834..ae8574f3c9 100644 --- a/libraries/audio/src/InjectedAudioRingBuffer.cpp +++ b/libraries/audio/src/InjectedAudioRingBuffer.cpp @@ -48,8 +48,6 @@ int InjectedAudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes unsigned int attenuationByte = *(currentBuffer++); _attenuationRatio = attenuationByte / (float) MAX_INJECTOR_VOLUME; - qDebug() << "Copying" << numBytes - (currentBuffer - sourceBuffer) << "for injected ring buffer\n"; - currentBuffer += writeData((char*) currentBuffer, numBytes - (currentBuffer - sourceBuffer)); return currentBuffer - sourceBuffer; diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index 4be6b80265..7037eb2154 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -66,6 +66,10 @@ bool PositionalAudioRingBuffer::shouldBeAddedToMix(int numJitterBufferSamples) { } else { // good buffer, add this to the mix _isStarved = false; + + // since we've read data from ring buffer at least once - we've started + _hasStarted = true; + return true; } diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index 0362561fff..02f8aaef9c 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -3,7 +3,7 @@ // hifi // // Created by Stephen Birarda on 1/2/2014. -// Copyright (c) 2013 HighFidelity, Inc. All rights reserved. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. // #include @@ -12,7 +12,9 @@ #include "Sound.h" -Sound::Sound(const QUrl& sampleURL) { +Sound::Sound(const QUrl& sampleURL, QObject* parent) : + QObject(parent) +{ // assume we have a QApplication or QCoreApplication instance and use the // QNetworkAccess manager to grab the raw audio file at the given URL diff --git a/libraries/audio/src/Sound.h b/libraries/audio/src/Sound.h index e9a6e856ba..82b59c8b49 100644 --- a/libraries/audio/src/Sound.h +++ b/libraries/audio/src/Sound.h @@ -3,7 +3,7 @@ // hifi // // Created by Stephen Birarda on 1/2/2014. -// Copyright (c) 2013 HighFidelity, Inc. All rights reserved. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. // #ifndef __hifi__Sound__ @@ -16,7 +16,7 @@ class QNetworkReply; class Sound : public QObject { Q_OBJECT public: - Sound(const QUrl& sampleURL); + Sound(const QUrl& sampleURL, QObject* parent = 0); const QByteArray& getByteArray() { return _byteArray; } private: diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index eec0c22359..64bcb93696 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -20,12 +20,20 @@ #include #include +#include + #include "ScriptEngine.h" int ScriptEngine::_scriptNumber = 1; VoxelsScriptingInterface ScriptEngine::_voxelsScriptingInterface; ParticlesScriptingInterface ScriptEngine::_particlesScriptingInterface; +static QScriptValue soundConstructor(QScriptContext* context, QScriptEngine* engine) { + QUrl soundURL = QUrl(context->argument(0).toString()); + QScriptValue soundScriptValue = engine->newQObject(new Sound(soundURL), QScriptEngine::ScriptOwnership); + + return soundScriptValue; +} ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, const char* scriptMenuName, AbstractMenuInterface* menu, @@ -82,6 +90,8 @@ bool ScriptEngine::setScriptContents(const QString& scriptContents) { return true; } +Q_SCRIPT_DECLARE_QMETAOBJECT(AudioInjectorOptions, QObject*) + void ScriptEngine::run() { _isRunning = true; QScriptEngine engine; @@ -101,6 +111,17 @@ void ScriptEngine::run() { QScriptValue particleScripterValue = engine.newQObject(&_particlesScriptingInterface); engine.globalObject().setProperty("Particles", particleScripterValue); + + QScriptValue soundConstructorValue = engine.newFunction(soundConstructor); + QScriptValue soundMetaObject = engine.newQMetaObject(&Sound::staticMetaObject, soundConstructorValue); + engine.globalObject().setProperty("Sound", soundMetaObject); + + QScriptValue injectionOptionValue = engine.scriptValueFromQMetaObject(); + engine.globalObject().setProperty("AudioInjectionOptions", injectionOptionValue); + + QScriptValue audioScriptingInterfaceValue = engine.newQObject(&_audioScriptingInterface); + engine.globalObject().setProperty("Audio", audioScriptingInterfaceValue); + if (_controllerScriptingInterface) { QScriptValue controllerScripterValue = engine.newQObject(_controllerScriptingInterface); engine.globalObject().setProperty("Controller", controllerScripterValue); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 44172de180..49ed913744 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -16,8 +16,10 @@ #include #include +#include #include #include + #include "AbstractControllerScriptingInterface.h" const QString NO_SCRIPT(""); @@ -61,6 +63,7 @@ private: static VoxelsScriptingInterface _voxelsScriptingInterface; static ParticlesScriptingInterface _particlesScriptingInterface; AbstractControllerScriptingInterface* _controllerScriptingInterface; + AudioScriptingInterface _audioScriptingInterface; bool _wantMenuItems; QString _scriptMenuName; AbstractMenuInterface* _menu; diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index b5d3d80e2f..4d91a21be7 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -12,6 +12,7 @@ #define hifi_RegisteredMetaTypes_h #include + #include #include "SharedUtil.h"