Merge pull request #4276 from birarda/master

repairs to memory management for AudioInjectors
This commit is contained in:
Andrew Meadows 2015-02-13 10:15:07 -08:00
commit d69efa03d8
22 changed files with 317 additions and 209 deletions

View file

@ -125,6 +125,8 @@ if (NOT ANDROID)
add_subdirectory(interface)
add_subdirectory(tests)
add_subdirectory(tools)
elseif (ANDROID OR DESKTOP_GVR)
endif ()
if (ANDROID OR DESKTOP_GVR)
add_subdirectory(gvr-interface)
endif ()

View file

@ -32,10 +32,4 @@ Script.update.connect(function() {
injector = Audio.playSound(sound, audioOptions);
print("Playing: " + injector);
}
});
Script.scriptEnding.connect(function() {
if (injector !== null) {
injector.stop();
}
});

View file

@ -85,10 +85,10 @@ function checkHands(deltaTime) {
var chord = Controller.getTriggerValue(chordHand);
if (volume > 1.0) volume = 1.0;
if ((chord > 0.1) && Audio.isInjectorPlaying(soundPlaying)) {
if ((chord > 0.1) && soundPlaying.isPlaying) {
// If chord finger trigger pulled, stop current chord
print("stopped sound");
Audio.stopInjector(soundPlaying);
soundPlaying.stop();
}
var BUTTON_COUNT = 6;
@ -132,16 +132,21 @@ function checkHands(deltaTime) {
}
function playChord(position, volume) {
if (Audio.isInjectorPlaying(soundPlaying)) {
if (soundPlaying.isPlaying) {
print("stopped sound");
Audio.stopInjector(soundPlaying);
soundPlaying.stop();
}
print("Played sound: " + whichChord + " at volume " + options.volume);
soundPlaying = Audio.playSound(chords[guitarSelector + whichChord], {
position: position,
volume: volume
});
if (!soundPlaying) {
soundPlaying = Audio.playSound(chords[guitarSelector + whichChord], {
position: position,
volume: volume
});
} else {
soundPlaying.restart();
}
}
function keyPressEvent(event) {

View file

@ -48,7 +48,8 @@
this.turnSounds = new Array();
this.moveSound = null;
this.turnSound = null;
this.injector = null;
this.moveInjector = null;
this.turnInjector = null;
var debug = false;
var displayRotateTargets = true; // change to false if you don't want the rotate targets
@ -92,9 +93,14 @@
}
if (this.moveSound && this.moveSound.downloaded) {
if (debug) {
print("playMoveSound() --- calling this.injector = Audio.playSound(this.moveSound...)");
print("playMoveSound() --- calling this.moveInjector = Audio.playSound(this.moveSound...)");
}
if (!this.moveInjector) {
this.moveInjector = Audio.playSound(this.moveSound, { position: this.properties.position, loop: true, volume: 0.1 });
} else {
this.moveInjector.restart();
}
this.injector = Audio.playSound(this.moveSound, { position: this.properties.position, loop: true, volume: 0.1 });
}
}
@ -105,9 +111,13 @@
}
if (this.turnSound && this.turnSound.downloaded) {
if (debug) {
print("playTurnSound() --- calling this.injector = Audio.playSound(this.turnSound...)");
print("playTurnSound() --- calling this.turnInjector = Audio.playSound(this.turnSound...)");
}
if (!this.turnInjector) {
this.turnInjector = Audio.playSound(this.turnSound, { position: this.properties.position, loop: true, volume: 0.1 });
} else {
this.turnInjector.restart();
}
this.injector = Audio.playSound(this.turnSound, { position: this.properties.position, loop: true, volume: 0.1 });
}
}
@ -116,9 +126,11 @@
if (debug) {
print("stopSound()");
}
if (this.injector) {
Audio.stopInjector(this.injector);
this.injector = null;
if (this.turnInjector) {
this.turnInjector.stop();
}
if (this.moveInjector) {
this.moveInjector.stop();
}
}
@ -174,7 +186,7 @@
this.move = function(mouseEvent) {
this.updatePosition(mouseEvent);
if (this.injector === null) {
if (this.moveInjector === null || !this.moveInjector.isPlaying) {
this.playMoveSound();
}
};
@ -233,7 +245,7 @@
}
}
if (this.injector === null) {
if (this.turnInjector === null || !this.turnInjector.isPlaying) {
this.playTurnSound();
}
};

View file

@ -75,14 +75,14 @@ function maybePlaySound(deltaTime) {
//print("number playing = " + numPlaying);
}
for (var i = 0; i < playing.length; i++) {
if (!Audio.isInjectorPlaying(playing[i].audioId)) {
if (!playing[i].audioId.isPlaying) {
Entities.deleteEntity(playing[i].entityId);
if (useLights) {
Entities.deleteEntity(playing[i].lightId);
}
playing.splice(i, 1);
} else {
var loudness = Audio.getLoudness(playing[i].audioId);
var loudness = playing[i].audioId.loudness;
var newColor = { red: playing[i].color.red, green: playing[i].color.green, blue: playing[i].color.blue };
if (loudness > 0.05) {
newColor.red *= (1.0 - loudness);

View file

@ -76,9 +76,6 @@ function scriptEnding() {
if (entity != null) {
Entities.deleteEntity(entity);
}
if (injector != null) {
injector.stop();
}
}
Script.update.connect(update);

View file

@ -55,7 +55,9 @@ var droneSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/drone.st
var currentDrone = null;
var latinSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/latin.stereo.raw")
var latinInjector = null;
var elevatorSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/elevator.stereo.raw")
var elevatorInjector = null;
var currentMuzakInjector = null;
var currentSound = null;
@ -140,7 +142,11 @@ function drawLobby() {
if (droneSound.downloaded) {
// start the drone sound
currentDrone = Audio.playSound(droneSound, { stereo: true, loop: true, localOnly: true, volume: DRONE_VOLUME });
if (!currentDrone) {
currentDrone = Audio.playSound(droneSound, { stereo: true, loop: true, localOnly: true, volume: DRONE_VOLUME });
} else {
currentDrone.restart();
}
}
// start one of our muzak sounds
@ -173,6 +179,26 @@ function changeLobbyTextures() {
var MUZAK_VOLUME = 0.1;
function playCurrentSound(secondOffset) {
if (currentSound == latinSound) {
if (!latinInjector) {
latinInjector = Audio.playSound(latinSound, { localOnly: true, secondOffset: secondOffset, volume: MUZAK_VOLUME });
} else {
latinInjector.restart();
}
currentMuzakInjector = latinInjector;
} else if (currentSound == elevatorSound) {
if (!elevatorInjector) {
elevatorInjector = Audio.playSound(elevatorSound, { localOnly: true, secondOffset: secondOffset, volume: MUZAK_VOLUME });
} else {
elevatorInjector.restart();
}
currentMuzakInjector = elevatorInjector;
}
}
function playNextMuzak() {
if (panelWall) {
if (currentSound == latinSound) {
@ -184,8 +210,8 @@ function playNextMuzak() {
currentSound = latinSound;
}
}
currentMuzakInjector = Audio.playSound(currentSound, { localOnly: true, volume: MUZAK_VOLUME });
playCurrentSound(0);
}
}
@ -200,10 +226,11 @@ function playRandomMuzak() {
currentSound = elevatorSound;
}
if (currentSound) {
if (currentSound) {
// pick a random number of seconds from 0-10 to offset the muzak
var secondOffset = Math.random() * 10;
currentMuzakInjector = Audio.playSound(currentSound, { localOnly: true, secondOffset: secondOffset, volume: MUZAK_VOLUME });
playCurrentSound(secondOffset);
} else {
currentMuzakInjector = null;
}
@ -227,10 +254,9 @@ function cleanupLobby() {
panelWall = false;
orbShell = false;
Audio.stopInjector(currentDrone);
currentDrone = null;
currentDrone.stop();
currentMuzakInjector.stop();
Audio.stopInjector(currentMuzakInjector);
currentMuzakInjector = null;
places = {};
@ -354,7 +380,7 @@ function update(deltaTime) {
Overlays.editOverlay(descriptionText, { position: textOverlayPosition() });
// if the reticle is up then we may need to play the next muzak
if (currentMuzakInjector && !Audio.isInjectorPlaying(currentMuzakInjector)) {
if (currentMuzakInjector && !currentMuzakInjector.isPlaying) {
playNextMuzak();
}
}

View file

@ -21,20 +21,13 @@ var offset = Vec3.normalize(Quat.getFront(MyAvatar.orientation));
var position = Vec3.sum(MyAvatar.position, offset);
function update(deltaTime) {
if (!Audio.isInjectorPlaying(soundPlaying)) {
soundPlaying = Audio.playSound(sound, {
position: position,
loop: true
});
print("Started sound loop");
}
}
function scriptEnding() {
if (Audio.isInjectorPlaying(soundPlaying)) {
Audio.stopInjector(soundPlaying);
print("Stopped sound loop");
}
if (sound.downloaded && !soundPlaying) {
print("Started sound loop");
soundPlaying = Audio.playSound(sound, {
position: position,
loop: true
});
}
}
Script.update.connect(update);

View file

@ -32,14 +32,13 @@ var sound = Audio.playSound(soundClip, { position: orbitCenter, loop: true, volu
function update(deltaTime) {
time += deltaTime;
currentPosition = { x: orbitCenter.x + Math.cos(time * SPEED) * RADIUS, y: orbitCenter.y, z: orbitCenter.z + Math.sin(time * SPEED) * RADIUS };
trailingLoudness = 0.9 * trailingLoudness + 0.1 * Audio.getLoudness(sound);
trailingLoudness = 0.9 * trailingLoudness + 0.1 * sound.loudness;
Entities.editEntity( objectId, { position: currentPosition, color: { red: Math.min(trailingLoudness * 2000, 255), green: 0, blue: 0 } } );
Audio.setInjectorOptions(sound, { position: currentPosition });
sound.setOptions({ position: currentPosition });
}
Script.scriptEnding.connect(function() {
Entities.deleteEntity(objectId);
Audio.stopInjector(sound);
});
Script.update.connect(update);

View file

@ -30,7 +30,7 @@ var playing = false;
var ball = false;
function maybePlaySound(deltaTime) {
if (sound.downloaded) {
if (sound.downloaded && !soundPlaying) {
var properties = {
type: "Sphere",
position: options.position,
@ -45,11 +45,9 @@ function maybePlaySound(deltaTime) {
}
function scriptEnding() {
if (Audio.isInjectorPlaying(soundPlaying)) {
Audio.stopInjector(soundPlaying);
Entities.deleteEntity(ball);
print("Stopped sound.");
}
if (ball) {
Entities.deleteEntity(ball);
}
}
// Connect a call back that happens every frame

View file

@ -541,9 +541,6 @@ Application::~Application() {
_nodeThread->quit();
_nodeThread->wait();
// kill any audio injectors that are still around
AudioScriptingInterface::getInstance().stopAllInjectors();
auto audioIO = DependencyManager::get<AudioClient>();
// stop the audio process

View file

@ -974,14 +974,16 @@ bool AudioClient::outputLocalInjector(bool isStereo, qreal volume, AudioInjector
QAudioOutput* localOutput = new QAudioOutput(getNamedAudioDeviceForMode(QAudio::AudioOutput, _outputAudioDeviceName),
localFormat,
injector);
injector->getLocalBuffer());
localOutput->setVolume(volume);
// move the localOutput to the same thread as the local injector buffer
localOutput->moveToThread(injector->getLocalBuffer()->thread());
// have it be cleaned up when that injector is done
connect(injector, &AudioInjector::finished, localOutput, &QAudioOutput::stop);
// have it be stopped when that local buffer is about to close
connect(injector->getLocalBuffer(), &AudioInjectorLocalBuffer::bufferEmpty, localOutput, &QAudioOutput::stop);
connect(injector->getLocalBuffer(), &QIODevice::aboutToClose, localOutput, &QAudioOutput::stop);
qDebug() << "Starting QAudioOutput for local injector" << localOutput;
@ -992,7 +994,6 @@ bool AudioClient::outputLocalInjector(bool isStereo, qreal volume, AudioInjector
return false;
}
void AudioClient::outputFormatChanged() {
int outputFormatChannelCountTimesSampleRate = _outputFormat.channelCount() * _outputFormat.sampleRate();
_outputFrameSize = AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * outputFormatChannelCountTimesSampleRate / _desiredOutputFormat.sampleRate();

View file

@ -9,6 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QtCore/QCoreApplication>
#include <QtCore/QDataStream>
#include <NodeList.h>
@ -21,79 +22,77 @@
#include "AudioInjector.h"
QScriptValue injectorToScriptValue(QScriptEngine* engine, AudioInjector* const& in) {
return engine->newQObject(in);
}
void injectorFromScriptValue(const QScriptValue& object, AudioInjector*& out) {
out = qobject_cast<AudioInjector*>(object.toQObject());
}
AudioInjector::AudioInjector(QObject* parent) :
QObject(parent),
_options(),
_shouldStop(false),
_loudness(0.0f),
_isFinished(false),
_currentSendPosition(0),
_localBuffer(NULL)
QObject(parent)
{
}
AudioInjector::AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions) :
_audioData(sound->getByteArray()),
_options(injectorOptions),
_shouldStop(false),
_loudness(0.0f),
_isFinished(false),
_currentSendPosition(0),
_localBuffer(NULL)
_options(injectorOptions)
{
}
AudioInjector::AudioInjector(const QByteArray& audioData, const AudioInjectorOptions& injectorOptions) :
_audioData(audioData),
_options(injectorOptions),
_shouldStop(false),
_loudness(0.0f),
_isFinished(false),
_currentSendPosition(0),
_localBuffer(NULL)
_options(injectorOptions)
{
}
AudioInjector::~AudioInjector() {
if (_localBuffer) {
_localBuffer->stop();
void AudioInjector::setIsFinished(bool isFinished) {
_isFinished = isFinished;
if (_isFinished) {
emit finished();
if (_localBuffer) {
_localBuffer->stop();
_localBuffer->deleteLater();
_localBuffer = NULL;
}
_isStarted = false;
_shouldStop = false;
if (_shouldDeleteAfterFinish) {
// we've been asked to delete after finishing, trigger a queued deleteLater here
qDebug() << "AudioInjector triggering delete from setIsFinished";
QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection);
}
}
}
void AudioInjector::setOptions(AudioInjectorOptions& options) {
_options = options;
}
float AudioInjector::getLoudness() {
return _loudness;
}
void AudioInjector::injectAudio() {
// check if we need to offset the sound by some number of seconds
if (_options.secondOffset > 0.0f) {
if (!_isStarted) {
// check if we need to offset the sound by some number of seconds
if (_options.secondOffset > 0.0f) {
// convert the offset into a number of bytes
int byteOffset = (int) floorf(AudioConstants::SAMPLE_RATE * _options.secondOffset * (_options.stereo ? 2.0f : 1.0f));
byteOffset *= sizeof(int16_t);
_currentSendPosition = byteOffset;
} else {
_currentSendPosition = 0;
}
// convert the offset into a number of bytes
int byteOffset = (int) floorf(AudioConstants::SAMPLE_RATE * _options.secondOffset * (_options.stereo ? 2.0f : 1.0f));
byteOffset *= sizeof(int16_t);
_currentSendPosition = byteOffset;
}
if (_options.localOnly) {
injectLocally();
if (_options.localOnly) {
injectLocally();
} else {
injectToMixer();
}
} else {
injectToMixer();
}
qDebug() << "AudioInjector::injectAudio called but already started.";
}
}
void AudioInjector::restart() {
qDebug() << "Restarting an AudioInjector by stopping and starting over.";
stop();
setIsFinished(false);
QMetaObject::invokeMethod(this, "injectAudio", Qt::QueuedConnection);
}
void AudioInjector::injectLocally() {
@ -102,6 +101,7 @@ void AudioInjector::injectLocally() {
if (_audioData.size() > 0) {
_localBuffer = new AudioInjectorLocalBuffer(_audioData, this);
_localBuffer->open(QIODevice::ReadOnly);
_localBuffer->setShouldLoop(_options.loop);
@ -236,6 +236,14 @@ void AudioInjector::injectToMixer() {
// send two packets before the first sleep so the mixer can start playback right away
if (_currentSendPosition != bytesToCopy && _currentSendPosition < _audioData.size()) {
// process events in case we have been told to stop and be deleted
QCoreApplication::processEvents();
if (_shouldStop) {
break;
}
// not the first packet and not done
// sleep for the appropriate time
int usecToSleep = (++nextFrame * AudioConstants::NETWORK_FRAME_USECS) - timer.nsecsElapsed() / 1000;
@ -251,8 +259,7 @@ void AudioInjector::injectToMixer() {
}
}
_isFinished = true;
emit finished();
setIsFinished(true);
}
void AudioInjector::stop() {
@ -260,7 +267,11 @@ void AudioInjector::stop() {
if (_options.localOnly) {
// we're only a local injector, so we can say we are finished right away too
_isFinished = true;
emit finished();
setIsFinished(true);
}
}
void AudioInjector::stopAndDeleteLater() {
stop();
QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection);
}

View file

@ -13,6 +13,7 @@
#define hifi_AudioInjector_h
#include <QtCore/QObject>
#include <QtCore/QSharedPointer>
#include <QtCore/QThread>
#include <glm/glm.hpp>
@ -24,15 +25,18 @@
class AbstractAudioInterface;
// In order to make scripting cleaner for the AudioInjector, the script now holds on to the AudioInjector object
// until it dies.
class AudioInjector : public QObject {
Q_OBJECT
public:
AudioInjector(QObject* parent);
AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions);
AudioInjector(const QByteArray& audioData, const AudioInjectorOptions& injectorOptions);
~AudioInjector();
bool isFinished() const { return _isFinished; }
int getCurrentSendPosition() const { return _currentSendPosition; }
AudioInjectorLocalBuffer* getLocalBuffer() const { return _localBuffer; }
@ -41,30 +45,37 @@ public:
void setLocalAudioInterface(AbstractAudioInterface* localAudioInterface) { _localAudioInterface = localAudioInterface; }
public slots:
void injectAudio();
void restart();
void stop();
void setOptions(AudioInjectorOptions& options);
void triggerDeleteAfterFinish() { _shouldDeleteAfterFinish = true; }
void stopAndDeleteLater();
void setOptions(AudioInjectorOptions& options) { _options = options; }
void setCurrentSendPosition(int currentSendPosition) { _currentSendPosition = currentSendPosition; }
float getLoudness();
float getLoudness() const { return _loudness; }
bool isPlaying() const { return !_isFinished; }
signals:
void finished();
private:
void injectToMixer();
void injectLocally();
void setIsFinished(bool isFinished);
QByteArray _audioData;
AudioInjectorOptions _options;
bool _shouldStop;
float _loudness;
bool _isFinished;
int _currentSendPosition;
AbstractAudioInterface* _localAudioInterface;
AudioInjectorLocalBuffer* _localBuffer;
bool _shouldStop = false;
float _loudness = 0.0f;
bool _isStarted = false;
bool _isFinished = false;
bool _shouldDeleteAfterFinish = false;
int _currentSendPosition = 0;
AbstractAudioInterface* _localAudioInterface = NULL;
AudioInjectorLocalBuffer* _localBuffer = NULL;
};
Q_DECLARE_METATYPE(AudioInjector*)
QScriptValue injectorToScriptValue(QScriptEngine* engine, AudioInjector* const& in);
void injectorFromScriptValue(const QScriptValue& object, AudioInjector*& out);
#endif // hifi_AudioInjector_h

View file

@ -23,9 +23,18 @@ AudioInjectorLocalBuffer::AudioInjectorLocalBuffer(const QByteArray& rawAudioArr
void AudioInjectorLocalBuffer::stop() {
_isStopped = true;
QIODevice::close();
}
bool AudioInjectorLocalBuffer::seek(qint64 pos) {
if (_isStopped) {
return false;
} else {
return QIODevice::seek(pos);
}
}
qint64 AudioInjectorLocalBuffer::readData(char* data, qint64 maxSize) {
if (!_isStopped) {

View file

@ -21,6 +21,8 @@ public:
void stop();
bool seek(qint64 pos);
qint64 readData(char* data, qint64 maxSize);
qint64 writeData(const char* data, qint64 maxSize) { return 0; }

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "ScriptAudioInjector.h"
#include "AudioScriptingInterface.h"
void registerAudioMetaTypes(QScriptEngine* engine) {
@ -28,80 +30,45 @@ AudioScriptingInterface::AudioScriptingInterface() :
}
void AudioScriptingInterface::stopAllInjectors() {
QList<QPointer<AudioInjector> >::iterator injector = _activeInjectors.begin();
while (injector != _activeInjectors.end()) {
if (!injector->isNull()) {
injector->data()->stop();
while (injector->data() && !injector->data()->isFinished()) {
// wait for this injector to go down
}
}
injector = _activeInjectors.erase(injector);
ScriptAudioInjector* AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions& injectorOptions) {
AudioInjector* injector = NULL;
QMetaObject::invokeMethod(this, "invokedPlaySound", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(AudioInjector*, injector),
Q_ARG(Sound*, sound), Q_ARG(const AudioInjectorOptions&, injectorOptions));
if (injector) {
return new ScriptAudioInjector(injector);
} else {
return NULL;
}
}
AudioInjector* AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions& injectorOptions) {
AudioInjector* AudioScriptingInterface::invokedPlaySound(Sound* sound, const AudioInjectorOptions& injectorOptions) {
if (sound) {
// stereo option isn't set from script, this comes from sound metadata or filename
AudioInjectorOptions optionsCopy = injectorOptions;
optionsCopy.stereo = sound->isStereo();
AudioInjector* injector = new AudioInjector(sound, optionsCopy);
injector->setLocalAudioInterface(_localAudioInterface);
QThread* injectorThread = new QThread();
injectorThread->setObjectName("Audio Injector Thread");
AudioInjector* injector = new AudioInjector(sound, optionsCopy);
injector->setLocalAudioInterface(_localAudioInterface);
injector->moveToThread(injectorThread);
// start injecting when the injector thread starts
connect(injectorThread, &QThread::started, injector, &AudioInjector::injectAudio);
// connect the right slots and signals so that the AudioInjector is killed once the injection is complete
connect(injector, &AudioInjector::finished, injector, &AudioInjector::deleteLater);
connect(injector, &AudioInjector::finished, injectorThread, &QThread::quit);
connect(injector, &AudioInjector::finished, this, &AudioScriptingInterface::injectorStopped);
// connect the right slots and signals for AudioInjector and thread cleanup
connect(injector, &AudioInjector::destroyed, injectorThread, &QThread::quit);
connect(injectorThread, &QThread::finished, injectorThread, &QThread::deleteLater);
injectorThread->start();
_activeInjectors.append(QPointer<AudioInjector>(injector));
return injector;
} else {
qDebug() << "AudioScriptingInterface::playSound called with null Sound object.";
return NULL;
}
}
void AudioScriptingInterface::stopInjector(AudioInjector* injector) {
if (injector) {
injector->stop();
}
}
bool AudioScriptingInterface::isInjectorPlaying(AudioInjector* injector) {
return (injector != NULL);
}
void AudioScriptingInterface::setInjectorOptions(AudioInjector* injector, const AudioInjectorOptions& injectorOptions) {
AudioInjectorOptions optionsCopy = injectorOptions;
if (injector) {
injector->setOptions(optionsCopy);
}
}
float AudioScriptingInterface::getLoudness(AudioInjector* injector) {
if (injector) {
return injector->getLoudness();
} else {
return 0.0f;
}
}
void AudioScriptingInterface::injectorStopped() {
_activeInjectors.removeAll(QPointer<AudioInjector>(reinterpret_cast<AudioInjector*>(sender())));
}

View file

@ -12,40 +12,32 @@
#ifndef hifi_AudioScriptingInterface_h
#define hifi_AudioScriptingInterface_h
#include <qpointer.h>
#include <AbstractAudioInterface.h>
#include <AudioInjector.h>
#include <Sound.h>
#include "AbstractAudioInterface.h"
#include "AudioInjector.h"
#include "Sound.h"
class ScriptAudioInjector;
class AudioScriptingInterface : public QObject {
Q_OBJECT
public:
static AudioScriptingInterface& getInstance();
void stopAllInjectors();
void setLocalAudioInterface(AbstractAudioInterface* audioInterface) { _localAudioInterface = audioInterface; }
public slots:
static float getLoudness(AudioInjector* injector);
AudioInjector* playSound(Sound* sound, const AudioInjectorOptions& injectorOptions = AudioInjectorOptions());
void stopInjector(AudioInjector* injector);
bool isInjectorPlaying(AudioInjector* injector);
void setInjectorOptions(AudioInjector* injector, const AudioInjectorOptions& injectorOptions);
void injectorStopped();
protected:
// this method is protected to stop C++ callers from calling, but invokable from script
Q_INVOKABLE ScriptAudioInjector* playSound(Sound* sound, const AudioInjectorOptions& injectorOptions = AudioInjectorOptions());
signals:
void mutedByMixer();
void environmentMuted();
private slots:
AudioInjector* invokedPlaySound(Sound* sound, const AudioInjectorOptions& injectorOptions);
private:
AudioScriptingInterface();
QList< QPointer<AudioInjector> > _activeInjectors;
AbstractAudioInterface* _localAudioInterface;
};

View file

@ -0,0 +1,40 @@
//
// ScriptAudioInjector.cpp
// libraries/script-engine/src
//
// Created by Stephen Birarda on 2015-02-11.
// Copyright 2015 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 "ScriptAudioInjector.h"
QScriptValue injectorToScriptValue(QScriptEngine* engine, ScriptAudioInjector* const& in) {
// when the script goes down we want to cleanup the injector
QObject::connect(engine, &QScriptEngine::destroyed, in, &ScriptAudioInjector::stopInjectorImmediately);
return engine->newQObject(in, QScriptEngine::ScriptOwnership);
}
void injectorFromScriptValue(const QScriptValue& object, ScriptAudioInjector*& out) {
out = qobject_cast<ScriptAudioInjector*>(object.toQObject());
}
ScriptAudioInjector::ScriptAudioInjector(AudioInjector* injector) :
_injector(injector)
{
}
ScriptAudioInjector::~ScriptAudioInjector() {
if (!_injector.isNull()) {
// we've been asked to delete after finishing, trigger a queued deleteLater here
_injector->triggerDeleteAfterFinish();
}
}
void ScriptAudioInjector::stopInjectorImmediately() {
_injector->stopAndDeleteLater();
}

View file

@ -0,0 +1,52 @@
//
// ScriptAudioInjector.h
// libraries/script-engine/src
//
// Created by Stephen Birarda on 2015-02-11.
// Copyright 2015 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_ScriptAudioInjector_h
#define hifi_ScriptAudioInjector_h
#include <QtCore/QObject>
#include <AudioInjector.h>
class ScriptAudioInjector : public QObject {
Q_OBJECT
Q_PROPERTY(bool isPlaying READ isPlaying)
Q_PROPERTY(float loudness READ getLoudness)
public:
ScriptAudioInjector(AudioInjector* injector);
~ScriptAudioInjector();
public slots:
void restart() { _injector->restart(); }
void stop() { _injector->stop(); }
void setOptions(AudioInjectorOptions& options) { _injector->setOptions(options); }
float getLoudness() const { return _injector->getLoudness(); }
bool isPlaying() const { return _injector->isPlaying(); }
signals:
void finished();
protected slots:
void stopInjectorImmediately();
private:
QPointer<AudioInjector> _injector;
friend QScriptValue injectorToScriptValue(QScriptEngine* engine, ScriptAudioInjector* const& in);
};
Q_DECLARE_METATYPE(ScriptAudioInjector*)
QScriptValue injectorToScriptValue(QScriptEngine* engine, ScriptAudioInjector* const& in);
void injectorFromScriptValue(const QScriptValue& object, ScriptAudioInjector*& out);
#endif // hifi_ScriptAudioInjector_h

View file

@ -19,7 +19,6 @@
#include <AudioConstants.h>
#include <AudioEffectOptions.h>
#include <AudioInjector.h>
#include <AvatarData.h>
#include <Bitstream.h>
#include <CollisionInfo.h>
@ -35,6 +34,7 @@
#include "DataViewClass.h"
#include "EventTypes.h"
#include "MenuItemProperties.h"
#include "ScriptAudioInjector.h"
#include "ScriptEngine.h"
#include "TypedArrays.h"
#include "XMLHttpRequestClass.h"

View file

@ -19,13 +19,13 @@
#include <QtScript/QScriptEngine>
#include <AnimationCache.h>
#include <AudioScriptingInterface.h>
#include <AvatarData.h>
#include <AvatarHashMap.h>
#include <LimitedNodeList.h>
#include "AbstractControllerScriptingInterface.h"
#include "ArrayBufferClass.h"
#include "AudioScriptingInterface.h"
#include "Quat.h"
#include "ScriptUUID.h"
#include "Vec3.h"