diff options
Diffstat (limited to 'src/multimedia/audio/qaudiostatemachine.cpp')
-rw-r--r-- | src/multimedia/audio/qaudiostatemachine.cpp | 163 |
1 files changed, 49 insertions, 114 deletions
diff --git a/src/multimedia/audio/qaudiostatemachine.cpp b/src/multimedia/audio/qaudiostatemachine.cpp index 83716efd2..2d42200e6 100644 --- a/src/multimedia/audio/qaudiostatemachine.cpp +++ b/src/multimedia/audio/qaudiostatemachine.cpp @@ -11,37 +11,7 @@ QT_BEGIN_NAMESPACE using Notifier = QAudioStateMachine::Notifier; using namespace AudioStateMachineUtils; -struct QAudioStateMachine::Synchronizer { - QMutex m_mutex; - QWaitCondition m_condition; - - template <typename Changer> - void changeState(Changer&& changer) { - { - QMutexLocker locker(&m_mutex); - changer(); - } - - m_condition.notify_all(); - } - - void waitForOperationFinished(std::atomic<RawState>& state) - { - QMutexLocker locker(&m_mutex); - while (isWaitingState(state)) - m_condition.wait(&m_mutex); - } - - void waitForDrained(std::atomic<RawState>& state, std::chrono::milliseconds timeout) { - QMutexLocker locker(&m_mutex); - if (isDrainingState(state)) - m_condition.wait(&m_mutex, timeout.count()); - } -}; - -QAudioStateMachine::QAudioStateMachine(QAudioStateChangeNotifier ¬ifier, bool synchronize) : - m_notifier(¬ifier), - m_sychronizer(synchronize ? std::make_unique<Synchronizer>() : nullptr) +QAudioStateMachine::QAudioStateMachine(QAudioStateChangeNotifier ¬ifier) : m_notifier(¬ifier) { } @@ -49,98 +19,70 @@ QAudioStateMachine::~QAudioStateMachine() = default; QAudio::State QAudioStateMachine::state() const { - return toAudioState(m_state); + return toAudioState(m_state.load(std::memory_order_acquire)); } QAudio::Error QAudioStateMachine::error() const { - return m_error; + return toAudioError(m_state.load(std::memory_order_acquire)); } -Notifier QAudioStateMachine::changeState(std::pair<RawState, uint32_t> prevStatesSet, - RawState newState, QAudio::Error error, bool shouldDrain) +template <typename StatesChecker, typename NewState> +Notifier QAudioStateMachine::changeState(const StatesChecker &checker, const NewState &newState) { - auto checkState = [flags = prevStatesSet.second](RawState state) { - return (flags >> state) & 1; - }; - - if (!m_sychronizer) { - RawState prevState = prevStatesSet.first; - const auto exchanged = multipleCompareExchange( - m_state, prevState, newState, checkState); + if constexpr (std::is_same_v<RawState, NewState>) + return changeState(checker, [newState](RawState) { return newState; }); + else { + RawState prevState = m_state.load(std::memory_order_relaxed); + const auto exchanged = multipleCompareExchange(m_state, prevState, checker, newState); if (Q_LIKELY(exchanged)) - return { this, newState, prevState, error }; + return { this, newState(prevState), prevState }; return {}; } - - while (true) { - RawState prevState = prevStatesSet.first; - - const auto newWaitingState = newState | (shouldDrain ? WaitingFlags : InProgressFlag); - - const auto exchanged = multipleCompareExchange( - m_state, prevState, newWaitingState, [checkState](RawState state) { - return !isWaitingState(state) && checkState(state); - }); - - if (Q_LIKELY(exchanged)) - return { this, newState, prevState, error }; - - if (!isWaitingState(prevState)) - return {}; - - if (!checkState(fromWaitingState(prevState))) - return {}; - - m_sychronizer->waitForOperationFinished(m_state); - } } Notifier QAudioStateMachine::stop(QAudio::Error error, bool shouldDrain, bool forceUpdateError) { - auto result = changeState( - makeStatesSet(QAudio::ActiveState, QAudio::IdleState, QAudio::SuspendedState), - QAudio::StoppedState, error, shouldDrain); + auto statesChecker = + makeStatesChecker(QAudio::ActiveState, QAudio::IdleState, QAudio::SuspendedState, + forceUpdateError ? QAudio::StoppedState : QAudio::ActiveState); - if (!result && forceUpdateError) - setError(error); + const auto state = toRawState(QAudio::StoppedState, error); + auto getNewState = [&](RawState prevState) { + const bool shouldAddFlag = shouldDrain && toAudioState(prevState) == QAudio::ActiveState; + return shouldAddFlag ? addDrainingFlag(state) : state; + }; - return result; + return changeState(statesChecker, getNewState); } Notifier QAudioStateMachine::start(bool active) { - return changeState(makeStatesSet(QAudio::StoppedState), - active ? QAudio::ActiveState : QAudio::IdleState); + return changeState(makeStatesChecker(QAudio::StoppedState), + toRawState(active ? QAudio::ActiveState : QAudio::IdleState)); } -void QAudioStateMachine::waitForDrained(std::chrono::milliseconds timeout) +bool QAudioStateMachine::isActiveOrIdle() const { - if (m_sychronizer) - m_sychronizer->waitForDrained(m_state, timeout); + const auto state = this->state(); + return state == QAudio::ActiveState || state == QAudio::IdleState; } -void QAudioStateMachine::onDrained() +bool QAudioStateMachine::onDrained() { - if (m_sychronizer) - m_sychronizer->changeState([this]() { m_state &= ~DrainingFlag; }); + return changeState(isDrainingState, removeDrainingFlag); } bool QAudioStateMachine::isDraining() const { - return isDrainingState(m_state); -} - -bool QAudioStateMachine::isActiveOrIdle() const { - const auto state = this->state(); - return state == QAudio::ActiveState || state == QAudio::IdleState; + return isDrainingState(m_state.load(std::memory_order_acquire)); } std::pair<bool, bool> QAudioStateMachine::getDrainedAndStopped() const { - const RawState state = m_state; + const auto state = m_state.load(std::memory_order_acquire); return { !isDrainingState(state), toAudioState(state) == QAudio::StoppedState }; } @@ -149,11 +91,11 @@ Notifier QAudioStateMachine::suspend() // Due to the current documentation, we set QAudio::NoError. // TBD: leave the previous error should be more reasonable (IgnoreError) const auto error = QAudio::NoError; - auto result = changeState(makeStatesSet(QAudio::ActiveState, QAudio::IdleState), - QAudio::SuspendedState, error); + auto result = changeState(makeStatesChecker(QAudio::ActiveState, QAudio::IdleState), + toRawState(QAudio::SuspendedState, error)); if (result) - m_suspendedInState = result.prevState(); + m_suspendedInState = result.prevAudioState(); return result; } @@ -163,53 +105,46 @@ Notifier QAudioStateMachine::resume() // Due to the current documentation, we set QAudio::NoError. // TBD: leave the previous error should be more reasonable (IgnoreError) const auto error = QAudio::NoError; - return changeState(makeStatesSet(QAudio::SuspendedState), m_suspendedInState, error); + return changeState(makeStatesChecker(QAudio::SuspendedState), + toRawState(m_suspendedInState, error)); } Notifier QAudioStateMachine::activateFromIdle() { - return changeState(makeStatesSet(QAudio::IdleState), QAudio::ActiveState); + return changeState(makeStatesChecker(QAudio::IdleState), toRawState(QAudio::ActiveState)); } Notifier QAudioStateMachine::updateActiveOrIdle(bool isActive, QAudio::Error error) { const auto state = isActive ? QAudio::ActiveState : QAudio::IdleState; - return changeState(makeStatesSet(QAudio::ActiveState, QAudio::IdleState), state, error); + return changeState(makeStatesChecker(QAudio::ActiveState, QAudio::IdleState), + toRawState(state, error)); } -void QAudioStateMachine::setError(QAudio::Error error) +Notifier QAudioStateMachine::setError(QAudio::Error error) { - if (m_error.exchange(error) != error && m_notifier) - emit m_notifier->errorChanged(error); + auto fixState = [error](RawState prevState) { return setStateError(prevState, error); }; + return changeState([](RawState) { return true; }, fixState); } Notifier QAudioStateMachine::forceSetState(QAudio::State state, QAudio::Error error) { - return changeState(makeStatesSet(QAudio::ActiveState, QAudio::IdleState, QAudio::SuspendedState, - QAudio::StoppedState), - state, error); + return changeState([](RawState) { return true; }, toRawState(state, error)); } -void QAudioStateMachine::reset(RawState state, RawState prevState, QAudio::Error error) +void QAudioStateMachine::reset(RawState state, RawState prevState) { - Q_ASSERT(!isWaitingState(state)); - - if (!m_sychronizer && m_state != state) - return; - - const auto isErrorChanged = m_error.exchange(error) != error; - - if (m_sychronizer) - m_sychronizer->changeState([&](){ m_state = state; }); - auto notifier = m_notifier; - if (prevState != state && notifier) - emit notifier->stateChanged(toAudioState(state)); + const auto audioState = toAudioState(state); + const auto audioError = toAudioError(state); + + if (toAudioState(prevState) != audioState && notifier) + emit notifier->stateChanged(audioState); // check the notifier in case the object was deleted in - if (isErrorChanged && notifier) - emit notifier->errorChanged(error); + if (toAudioError(prevState) != audioError && notifier) + emit notifier->errorChanged(audioError); } QT_END_NAMESPACE |