avoid Qt QTimer thread bug by putting SoundCache on same thread

This commit is contained in:
Stephen Birarda 2014-11-13 14:47:28 -08:00
parent a771f72ed2
commit e10d132f75
11 changed files with 74 additions and 61 deletions

View file

@ -67,7 +67,7 @@ function maybePlaySound(deltaTime) {
lifetime: 10
});
}
playing.push({ audioId: Audio.playSound(birds[whichBird].sound, options), entityId: entityId, lightId: lightId, color: birds[whichBird].color });
}
if (playing.length != numPlaying) {
@ -159,8 +159,9 @@ function loadBirds() {
var SOUND_BASE_URL = "http://public.highfidelity.io/sounds/Animals/";
for (var i = 0; i < sound_filenames.length; i++) {
birds.push({ sound: new Sound(SOUND_BASE_URL + sound_filenames[i]),
color: colors[i]
} );
birds.push({
sound: SoundCache.getSound(SOUND_BASE_URL + sound_filenames[i]),
color: colors[i]
});
}
}

View file

@ -65,6 +65,7 @@
#include <PacketHeaders.h>
#include <PerfStat.h>
#include <ResourceCache.h>
#include <SoundCache.h>
#include <UserActivityLogger.h>
#include <UUID.h>
@ -3916,6 +3917,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance());
scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance());
scriptEngine->registerGlobalObject("AnimationCache", &_animationCache);
scriptEngine->registerGlobalObject("SoundCache", &SoundCache::getInstance());
scriptEngine->registerGlobalObject("AudioReflector", &_audioReflector);
scriptEngine->registerGlobalObject("Account", AccountScriptingInterface::getInstance());
scriptEngine->registerGlobalObject("Metavoxels", &_metavoxels);

View file

@ -43,28 +43,32 @@ void AudioScriptingInterface::stopAllInjectors() {
}
AudioInjector* AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions& injectorOptions) {
AudioInjector* injector = new AudioInjector(sound, injectorOptions);
injector->setLocalAudioInterface(_localAudioInterface);
QThread* injectorThread = new QThread();
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(injectorThread, &QThread::finished, injectorThread, &QThread::deleteLater);
injectorThread->start();
_activeInjectors.append(QPointer<AudioInjector>(injector));
return injector;
if (sound) {
AudioInjector* injector = new AudioInjector(sound, injectorOptions);
injector->setLocalAudioInterface(_localAudioInterface);
QThread* injectorThread = new QThread();
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(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) {

View file

@ -29,18 +29,19 @@
#include "AudioEditBuffer.h"
#include "Sound.h"
QScriptValue soundToScriptValue(QScriptEngine* engine, Sound* const& in) {
return engine->newQObject(in);
QScriptValue soundToScriptValue(QScriptEngine* engine, SharedSoundPointer const& in) {
return engine->newQObject(in.data());
}
void soundFromScriptValue(const QScriptValue& object, Sound*& out) {
out = qobject_cast<Sound*>(object.toQObject());
void soundFromScriptValue(const QScriptValue &object, SharedSoundPointer &out) {
out = SharedSoundPointer(qobject_cast<Sound*>(object.toQObject()));
qDebug() << "Sound from script value" << out.data();
}
Sound::Sound(const QUrl& url, bool isStereo) :
Resource(url),
_isStereo(isStereo)
_isStereo(isStereo),
_isReady(false)
{
}
@ -70,6 +71,8 @@ void Sound::downloadFinished(QNetworkReply* reply) {
} else {
qDebug() << "Network reply without 'Content-Type'.";
}
_isReady = true;
}
void Sound::downSample(const QByteArray& rawAudioByteArray) {

View file

@ -21,17 +21,19 @@
class Sound : public Resource {
Q_OBJECT
Q_PROPERTY(bool downloaded READ isLoaded)
Q_PROPERTY(bool downloaded READ isReady)
public:
Sound(const QUrl& url, bool isStereo = false);
bool isStereo() const { return _isStereo; }
bool isReady() const { return _isReady; }
const QByteArray& getByteArray() { return _byteArray; }
private:
QByteArray _byteArray;
bool _isStereo;
bool _isReady;
void trimFrames();
void downSample(const QByteArray& rawAudioByteArray);
@ -40,4 +42,11 @@ private:
virtual void downloadFinished(QNetworkReply* reply);
};
typedef QSharedPointer<Sound> SharedSoundPointer;
Q_DECLARE_METATYPE(SharedSoundPointer)
QScriptValue soundToScriptValue(QScriptEngine* engine, SharedSoundPointer const& in);
void soundFromScriptValue(const QScriptValue& object, SharedSoundPointer& out);
#endif // hifi_Sound_h

View file

@ -13,8 +13,17 @@
#include "SoundCache.h"
static int soundPointerMetaTypeId = qRegisterMetaType<SharedSoundPointer>();
SoundCache& SoundCache::getInstance() {
static SoundCache staticInstance;
return staticInstance;
}
SoundCache::SoundCache(QObject* parent) :
ResourceCache(parent) {
ResourceCache(parent)
{
}
SharedSoundPointer SoundCache::getSound(const QUrl& url) {
@ -24,10 +33,11 @@ SharedSoundPointer SoundCache::getSound(const QUrl& url) {
Q_RETURN_ARG(SharedSoundPointer, result), Q_ARG(const QUrl&, url));
return result;
}
qDebug() << "Requesting sound at" << url.toString() << "from SoundCache";
return getResource(url).staticCast<Sound>();
}
QSharedPointer<Resource> SoundCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
bool delayLoad, const void* extra) {
bool delayLoad, const void* extra) {
return QSharedPointer<Resource>(new Sound(url), &Resource::allReferencesCleared);
}

View file

@ -16,22 +16,19 @@
#include "Sound.h"
typedef QSharedPointer<Sound> SharedSoundPointer;
/// Scriptable interface for FBX animation loading.
/// Scriptable interface for sound loading.
class SoundCache : public ResourceCache {
Q_OBJECT
public:
SoundCache(QObject* parent = NULL);
static SoundCache& getInstance();
Q_INVOKABLE SharedSoundPointer getSound(const QUrl& url);
protected:
virtual QSharedPointer<Resource> createResource(const QUrl& url,
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra);
private:
SoundCache(QObject* parent = NULL);
};
Q_DECLARE_METATYPE(SharedSoundPointer)
#endif // hifi_SoundCache_h

View file

@ -168,7 +168,7 @@ void Player::setupAudioThread() {
_audioThread = new QThread();
_options.position = _avatar->getPosition();
_options.orientation = _avatar->getOrientation();
_injector.reset(new AudioInjector(_recording->getAudio(), _options), &QObject::deleteLater);
_injector.reset(new AudioInjector(_recording->getAudioData(), _options), &QObject::deleteLater);
_injector->moveToThread(_audioThread);
_audioThread->start();
QMetaObject::invokeMethod(_injector.data(), "injectAudio", Qt::QueuedConnection);

View file

@ -22,7 +22,9 @@
ResourceCache::ResourceCache(QObject* parent) :
QObject(parent),
_lastLRUKey(0) {
_lastLRUKey(0)
{
}
ResourceCache::~ResourceCache() {
@ -291,7 +293,7 @@ void Resource::makeRequest() {
connect(_reply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleDownloadProgress(qint64,qint64)));
connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleReplyError()));
connect(_reply, SIGNAL(finished()), SLOT(handleReplyFinished()));
_replyTimer = new QTimer(this);
connect(_replyTimer, SIGNAL(timeout()), SLOT(handleReplyTimeout()));
_replyTimer->setSingleShot(true);

View file

@ -27,7 +27,6 @@
#include <NetworkAccessManager.h>
#include <NodeList.h>
#include <PacketHeaders.h>
#include <Sound.h>
#include <UUID.h>
#include <VoxelConstants.h>
#include <VoxelDetail.h>
@ -47,14 +46,6 @@
VoxelsScriptingInterface ScriptEngine::_voxelsScriptingInterface;
EntityScriptingInterface ScriptEngine::_entityScriptingInterface;
static QScriptValue soundConstructor(QScriptContext* context, QScriptEngine* engine) {
QUrl soundURL = QUrl(context->argument(0).toString());
bool isStereo = context->argument(1).toBool();
QScriptValue soundScriptValue = engine->newQObject(new Sound(soundURL, isStereo), QScriptEngine::ScriptOwnership);
return soundScriptValue;
}
static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){
qDebug() << "script:print()<<" << context->argument(0).toString();
QString message = context->argument(0).toString()
@ -263,10 +254,6 @@ void ScriptEngine::init() {
QScriptValue printConstructorValue = newFunction(debugPrint);
globalObject().setProperty("print", printConstructorValue);
QScriptValue soundConstructorValue = newFunction(soundConstructor);
QScriptValue soundMetaObject = newQMetaObject(&Sound::staticMetaObject, soundConstructorValue);
globalObject().setProperty("Sound", soundMetaObject);
QScriptValue localVoxelsValue = scriptValueFromQMetaObject<LocalVoxels>();
globalObject().setProperty("LocalVoxels", localVoxelsValue);

View file

@ -119,8 +119,6 @@ QString LogHandler::printMessage(LogMsgType type, const QMessageLogContext& cont
char dateString[100];
strftime(dateString, sizeof(dateString), DATE_STRING_FORMAT, localTime);
prefixString.append(QString(" [%1]").arg(dateString));
if (_shouldOutputPID) {
prefixString.append(QString(" [%1").arg(getpid()));