mirror of
https://github.com/lubosz/overte.git
synced 2025-04-23 09:25:31 +02:00
Merge pull request #4276 from birarda/master
repairs to memory management for AudioInjectors
This commit is contained in:
commit
d69efa03d8
22 changed files with 317 additions and 209 deletions
|
@ -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 ()
|
|
@ -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();
|
||||
}
|
||||
});
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -76,9 +76,6 @@ function scriptEnding() {
|
|||
if (entity != null) {
|
||||
Entities.deleteEntity(entity);
|
||||
}
|
||||
if (injector != null) {
|
||||
injector.stop();
|
||||
}
|
||||
}
|
||||
|
||||
Script.update.connect(update);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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())));
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
40
libraries/script-engine/src/ScriptAudioInjector.cpp
Normal file
40
libraries/script-engine/src/ScriptAudioInjector.cpp
Normal 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();
|
||||
}
|
52
libraries/script-engine/src/ScriptAudioInjector.h
Normal file
52
libraries/script-engine/src/ScriptAudioInjector.h
Normal 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
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue