1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qaudiostatemachine_p.h"
5#include "qaudiosystem_p.h"
6#include <qpointer.h>
7#include <qdebug.h>
8
9QT_BEGIN_NAMESPACE
10
11using Notifier = QAudioStateMachine::Notifier;
12using namespace AudioStateMachineUtils;
13
14QAudioStateMachine::QAudioStateMachine(QAudioStateChangeNotifier &notifier) : m_notifier(&notifier)
15{
16}
17
18QAudioStateMachine::~QAudioStateMachine() = default;
19
20QAudio::State QAudioStateMachine::state() const
21{
22 return toAudioState(state: m_state.load(m: std::memory_order_acquire));
23}
24
25QAudio::Error QAudioStateMachine::error() const
26{
27 return toAudioError(state: m_state.load(m: std::memory_order_acquire));
28}
29
30template <typename StatesChecker, typename NewState>
31Notifier QAudioStateMachine::changeState(const StatesChecker &checker, const NewState &newState)
32{
33 if constexpr (std::is_same_v<RawState, NewState>)
34 return changeState(checker, [newState](RawState) { return newState; });
35 else {
36 RawState prevState = m_state.load(m: std::memory_order_relaxed);
37 const auto exchanged = multipleCompareExchange(m_state, prevState, checker, newState);
38
39 if (Q_LIKELY(exchanged))
40 return { this, newState(prevState), prevState };
41
42 return {};
43 }
44}
45
46Notifier QAudioStateMachine::stop(QAudio::Error error, bool shouldDrain, bool forceUpdateError)
47{
48 auto statesChecker =
49 makeStatesChecker(states: QAudio::ActiveState, states: QAudio::IdleState, states: QAudio::SuspendedState,
50 states: forceUpdateError ? QAudio::StoppedState : QAudio::ActiveState);
51
52 const auto state = toRawState(state: QAudio::StoppedState, error);
53 auto getNewState = [&](RawState prevState) {
54 const bool shouldAddFlag = shouldDrain && toAudioState(state: prevState) == QAudio::ActiveState;
55 return shouldAddFlag ? addDrainingFlag(state) : state;
56 };
57
58 return changeState(checker: statesChecker, newState: getNewState);
59}
60
61Notifier QAudioStateMachine::start(RunningState activeOrIdle)
62{
63 return changeState(checker: makeStatesChecker(states: QAudio::StoppedState),
64 newState: toRawState(state: static_cast<QtAudio::State>(activeOrIdle)));
65}
66
67bool QAudioStateMachine::isActiveOrIdle() const
68{
69 const auto state = this->state();
70 return state == QAudio::ActiveState || state == QAudio::IdleState;
71}
72
73bool QAudioStateMachine::onDrained()
74{
75 return changeState(checker&: isDrainingState, newState&: removeDrainingFlag);
76}
77
78bool QAudioStateMachine::isDraining() const
79{
80 return isDrainingState(state: m_state.load(m: std::memory_order_acquire));
81}
82
83std::pair<bool, bool> QAudioStateMachine::getDrainedAndStopped() const
84{
85 const auto state = m_state.load(m: std::memory_order_acquire);
86 return { !isDrainingState(state), toAudioState(state) == QAudio::StoppedState };
87}
88
89Notifier QAudioStateMachine::suspend()
90{
91 // Due to the current documentation, we set QAudio::NoError.
92 // TBD: leave the previous error should be more reasonable (IgnoreError)
93 const auto error = QAudio::NoError;
94 auto result = changeState(checker: makeStatesChecker(states: QAudio::ActiveState, states: QAudio::IdleState),
95 newState: toRawState(state: QAudio::SuspendedState, error));
96
97 if (result)
98 m_suspendedInState = result.prevAudioState();
99
100 return result;
101}
102
103Notifier QAudioStateMachine::resume()
104{
105 // Due to the current documentation, we set QAudio::NoError.
106 // TBD: leave the previous error should be more reasonable (IgnoreError)
107 const auto error = QAudio::NoError;
108 return changeState(checker: makeStatesChecker(states: QAudio::SuspendedState),
109 newState: toRawState(state: m_suspendedInState, error));
110}
111
112Notifier QAudioStateMachine::activateFromIdle()
113{
114 return changeState(checker: makeStatesChecker(states: QAudio::IdleState), newState: toRawState(state: QAudio::ActiveState));
115}
116
117Notifier QAudioStateMachine::updateActiveOrIdle(RunningState activeOrIdle, QAudio::Error error)
118{
119 return changeState(checker: makeStatesChecker(states: QAudio::ActiveState, states: QAudio::IdleState),
120 newState: toRawState(state: static_cast<QtAudio::State>(activeOrIdle), error));
121}
122
123Notifier QAudioStateMachine::setError(QAudio::Error error)
124{
125 auto fixState = [error](RawState prevState) { return setStateError(state: prevState, error); };
126 return changeState(checker: [](RawState) { return true; }, newState: fixState);
127}
128
129Notifier QAudioStateMachine::forceSetState(QAudio::State state, QAudio::Error error)
130{
131 return changeState(checker: [](RawState) { return true; }, newState: toRawState(state, error));
132}
133
134void QAudioStateMachine::reset(RawState state, RawState prevState)
135{
136 auto notifier = m_notifier;
137
138 const auto audioState = toAudioState(state);
139 const auto audioError = toAudioError(state);
140
141 if (toAudioState(state: prevState) != audioState && notifier)
142 emit notifier->stateChanged(state: audioState);
143
144 // check the notifier in case the object was deleted in
145 if (toAudioError(state: prevState) != audioError && notifier)
146 emit notifier->errorChanged(error: audioError);
147}
148
149QT_END_NAMESPACE
150

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtmultimedia/src/multimedia/audio/qaudiostatemachine.cpp