Updated to deal with streaming out of both buffers

So gotta keep track of when finished streaming to network and
locally separately.  That means the State needed to be more of a
bitflag and less of an enum.
This commit is contained in:
David Kelly 2016-07-15 09:06:13 -07:00
parent 1c6b5e89ac
commit e1212c54cb
3 changed files with 95 additions and 45 deletions

View file

@ -954,7 +954,6 @@ void AudioClient::processReceivedSamples(const QByteArray& decodedBuffer, QByteA
if (hasReverb) {
assert(_outputFormat.channelCount() == 2);
updateReverbOptions();
qDebug() << "handling reverb";
_listenerReverb.render(outputSamples, outputSamples, numDeviceOutputSamples/2);
}
}

View file

@ -28,6 +28,15 @@
int audioInjectorPtrMetaTypeId = qRegisterMetaType<AudioInjector*>();
AudioInjectorState operator& (AudioInjectorState lhs, AudioInjectorState rhs) {
return static_cast<AudioInjectorState>(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
};
AudioInjectorState& operator&= (AudioInjectorState& lhs, AudioInjectorState rhs) {
lhs = static_cast<AudioInjectorState>(static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs));
return lhs;
};
AudioInjector::AudioInjector(QObject* parent) :
QObject(parent)
{
@ -56,10 +65,26 @@ void AudioInjector::setOptions(const AudioInjectorOptions& options) {
_options.stereo = currentlyStereo;
}
void AudioInjector::finishNetworkInjection() {
_state &= AudioInjectorState::NetworkInjectionFinished;
// if we are already finished with local
// injection, then we are finished
if((_state & AudioInjectorState::LocalInjectionFinished) == AudioInjectorState::LocalInjectionFinished) {
finish();
}
}
void AudioInjector::finishLocalInjection() {
_state &= AudioInjectorState::LocalInjectionFinished;
if(_options.localOnly || ((_state & AudioInjectorState::NetworkInjectionFinished) == AudioInjectorState::NetworkInjectionFinished)) {
finish();
}
}
void AudioInjector::finish() {
bool shouldDelete = (_state == State::NotFinishedWithPendingDelete);
_state = State::Finished;
bool shouldDelete = ((_state & AudioInjectorState::PendingDelete) == AudioInjectorState::PendingDelete);
_state &= AudioInjectorState::Finished;
emit finished();
@ -121,23 +146,31 @@ void AudioInjector::restart() {
_hasSentFirstFrame = false;
// check our state to decide if we need extra handling for the restart request
if (_state == State::Finished) {
if ((_state & AudioInjectorState::Finished) == AudioInjectorState::Finished) {
// we finished playing, need to reset state so we can get going again
_hasSetup = false;
_shouldStop = false;
_state = State::NotFinished;
_state = AudioInjectorState::NotFinished;
// call inject audio to start injection over again
setupInjection();
// if we're a local injector, just inject again
if (_options.localOnly) {
injectLocally();
} else {
// wake the AudioInjectorManager back up if it's stuck waiting
if (!injectorManager->restartFinishedInjector(this)) {
_state = State::Finished; // we're not playing, so reset the state used by isPlaying.
// inject locally
if(injectLocally()) {
// if not localOnly, wake the AudioInjectorManager back up if it is stuck waiting
if (!_options.localOnly) {
if (!injectorManager->restartFinishedInjector(this)) {
// TODO: this logic seems to remove the pending delete,
// which makes me wonder about the deleteLater calls
_state = AudioInjectorState::Finished; // we're not playing, so reset the state used by isPlaying.
}
}
} else {
// TODO: this logic seems to remove the pending delete,
// which makes me wonder about the deleteLater calls
_state = AudioInjectorState::Finished; // we failed to play, so we are finished again
}
}
}
@ -183,7 +216,7 @@ static const int64_t NEXT_FRAME_DELTA_ERROR_OR_FINISHED = -1;
static const int64_t NEXT_FRAME_DELTA_IMMEDIATELY = 0;
int64_t AudioInjector::injectNextFrame() {
if (_state == AudioInjector::State::Finished) {
if ((_state & AudioInjectorState::NetworkInjectionFinished) == AudioInjectorState::NetworkInjectionFinished) {
qDebug() << "AudioInjector::injectNextFrame called but AudioInjector has finished and was not restarted. Returning.";
return NEXT_FRAME_DELTA_ERROR_OR_FINISHED;
}
@ -234,8 +267,10 @@ int64_t AudioInjector::injectNextFrame() {
// pack the stereo/mono type of the stream
audioPacketStream << _options.stereo;
// pack the flag for loopback
uchar loopbackFlag = (uchar)true;
// pack the flag for loopback. Now, we don't loopback
// and _always_ play locally, so loopbackFlag should be
// false always.
uchar loopbackFlag = (uchar)false;
audioPacketStream << loopbackFlag;
// pack the position for injected audio
@ -333,7 +368,7 @@ int64_t AudioInjector::injectNextFrame() {
}
if (_currentSendOffset >= _audioData.size() && !_options.loop) {
finish();
finishNetworkInjection();
return NEXT_FRAME_DELTA_ERROR_OR_FINISHED;
}
@ -372,10 +407,10 @@ void AudioInjector::triggerDeleteAfterFinish() {
return;
}
if (_state == State::Finished) {
if (_state == AudioInjectorState::Finished) {
stopAndDeleteLater();
} else {
_state = State::NotFinishedWithPendingDelete;
_state &= AudioInjectorState::PendingDelete;
}
}
@ -421,7 +456,7 @@ AudioInjector* AudioInjector::playSoundAndDelete(const QByteArray& buffer, const
AudioInjector* sound = playSound(buffer, options, localInterface);
if (sound) {
sound->_state = AudioInjector::State::NotFinishedWithPendingDelete;
sound->_state &= AudioInjectorState::PendingDelete;
}
return sound;
@ -438,21 +473,23 @@ AudioInjector* AudioInjector::playSound(const QByteArray& buffer, const AudioInj
// setup parameters required for injection
injector->setupInjection();
if (options.localOnly) {
if (injector->injectLocally()) {
// local injection succeeded, return the pointer to injector
return injector;
} else {
// unable to inject locally, return a nullptr
return nullptr;
}
} else {
// attempt to thread the new injector
if (injectorManager->threadInjector(injector)) {
return injector;
} else {
// we failed to thread the new injector (we are at the max number of injector threads)
return nullptr;
}
// we always inject locally
//
if (!injector->injectLocally()) {
// failed, so don't bother sending to server
qDebug() << "AudioInjector::playSound failed to inject locally";
return nullptr;
}
// if localOnly, we are done, just return injector.
if (options.localOnly) {
return injector;
}
// send off to server for everyone else
if (!injectorManager->threadInjector(injector)) {
// we failed to thread the new injector (we are at the max number of injector threads)
qDebug() << "AudioInjector::playSound failed to thread injector";
}
return injector;
}

View file

@ -32,24 +32,35 @@
class AbstractAudioInterface;
class AudioInjectorManager;
enum class AudioInjectorState : uint8_t {
NotFinished = 1,
Finished = 2,
PendingDelete = 4,
LocalInjectionFinished = 8,
NetworkInjectionFinished = 16
};
AudioInjectorState operator& (AudioInjectorState lhs, AudioInjectorState rhs);
AudioInjectorState& operator&= (AudioInjectorState& lhs, AudioInjectorState rhs);
// 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:
enum class State : uint8_t {
NotFinished,
NotFinishedWithPendingDelete,
Finished
};
static const uint8_t NotFinished = 1;
static const uint8_t Finished = 2;
static const uint8_t PendingDelete = 4;
static const uint8_t LocalInjectionFinished = 8;
static const uint8_t NetworkInjectionFinished = 16;
AudioInjector(QObject* parent);
AudioInjector(const Sound& sound, const AudioInjectorOptions& injectorOptions);
AudioInjector(const QByteArray& audioData, const AudioInjectorOptions& injectorOptions);
bool isFinished() const { return _state == State::Finished; }
bool isFinished() const { return (_state & AudioInjectorState::Finished) == AudioInjectorState::Finished; }
int getCurrentSendOffset() const { return _currentSendOffset; }
void setCurrentSendOffset(int currentSendOffset) { _currentSendOffset = currentSendOffset; }
@ -78,8 +89,10 @@ public slots:
void setOptions(const AudioInjectorOptions& options);
float getLoudness() const { return _loudness; }
bool isPlaying() const { return _state == State::NotFinished || _state == State::NotFinishedWithPendingDelete; }
bool isPlaying() const { return (_state & AudioInjectorState::NotFinished) == AudioInjectorState::NotFinished; }
void finish();
void finishLocalInjection();
void finishNetworkInjection();
signals:
void finished();
@ -92,7 +105,7 @@ private:
QByteArray _audioData;
AudioInjectorOptions _options;
State _state { State::NotFinished };
AudioInjectorState _state { AudioInjectorState::NotFinished };
bool _hasSentFirstFrame { false };
bool _hasSetup { false };
bool _shouldStop { false };
@ -111,4 +124,5 @@ private:
friend class AudioInjectorManager;
};
#endif // hifi_AudioInjector_h