summaryrefslogtreecommitdiffstats
path: root/src/multimedia/audio/qaudiostatemachine_p.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/multimedia/audio/qaudiostatemachine_p.h')
-rw-r--r--src/multimedia/audio/qaudiostatemachine_p.h167
1 files changed, 167 insertions, 0 deletions
diff --git a/src/multimedia/audio/qaudiostatemachine_p.h b/src/multimedia/audio/qaudiostatemachine_p.h
new file mode 100644
index 000000000..44bef3ab0
--- /dev/null
+++ b/src/multimedia/audio/qaudiostatemachine_p.h
@@ -0,0 +1,167 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QAUDIOSTATEMACHINE_P_H
+#define QAUDIOSTATEMACHINE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtMultimedia/qaudio.h>
+
+#include <qmutex.h>
+#include <qwaitcondition.h>
+#include <qpointer.h>
+#include <atomic>
+#include <chrono>
+
+QT_BEGIN_NAMESPACE
+
+class QAudioStateChangeNotifier;
+
+/* QAudioStateMachine provides an opportunity to
+ * toggle QAudio::State with QAudio::Error in
+ * a thread-safe manner.
+ * The toggling functions return a guard,
+ * which scope guaranties thread safety (if synchronization enabled)
+ * and notifies about the change via
+ * QAudioStateChangeNotifier::stateChanged and errorChanged.
+ *
+ * The state machine is supposed to be used mostly in
+ * QAudioSink and QAudioSource implementations.
+ */
+class Q_MULTIMEDIA_EXPORT QAudioStateMachine
+{
+public:
+ using RawState = int;
+ class StateChangeGuard
+ {
+ public:
+ void reset()
+ {
+ if (auto stateMachine = std::exchange(m_stateMachine, nullptr))
+ stateMachine->reset(m_state, m_prevState, m_error);
+ }
+
+ ~StateChangeGuard() { reset(); }
+
+ StateChangeGuard(const StateChangeGuard &) = delete;
+ StateChangeGuard(StateChangeGuard &&other)
+ : m_stateMachine(std::exchange(other.m_stateMachine, nullptr)),
+ m_state(other.m_state),
+ m_prevState(other.m_prevState),
+ m_error(other.m_error)
+ {
+ }
+
+ operator bool() const { return m_stateMachine != nullptr; }
+
+ void setError(QAudio::Error error) { m_error = error; }
+
+ // Can be added make state changing more flexible
+ // but needs some investigation to ensure state change consistency
+ // The method is supposed to be used for sync read/write
+ // under "guard = updateActiveOrIdle(isActive)"
+ // void setState(QAudio::State state) { ... }
+
+ bool isStateChanged() const { return m_state != m_prevState; }
+
+ QAudio::State prevState() const { return QAudio::State(m_prevState); }
+
+ private:
+ StateChangeGuard(QAudioStateMachine *stateMachine = nullptr,
+ RawState state = QAudio::StoppedState,
+ RawState prevState = QAudio::StoppedState,
+ QAudio::Error error = QAudio::NoError)
+ : m_stateMachine(stateMachine), m_state(state), m_prevState(prevState), m_error(error)
+ {
+ }
+
+ private:
+ QAudioStateMachine *m_stateMachine;
+ RawState m_state;
+ const RawState m_prevState;
+ QAudio::Error m_error;
+
+ friend class QAudioStateMachine;
+ };
+
+ QAudioStateMachine(QAudioStateChangeNotifier &notifier, bool synchronize = true);
+
+ ~QAudioStateMachine();
+
+ QAudio::State state() const;
+
+ QAudio::Error error() const;
+
+ bool isDraining() const;
+
+ bool isActiveOrIdle() const;
+
+ // atomicaly checks if the state is stopped and marked as drained
+ std::pair<bool, bool> getDrainedAndStopped() const;
+
+ // waits if the method stop(error, true) has bee called
+ void waitForDrained(std::chrono::milliseconds timeout);
+
+ // mark as drained and wake up the method waitForDrained
+ void onDrained();
+
+ // Active/Idle/Suspended -> Stopped
+ StateChangeGuard stop(QAudio::Error error = QAudio::NoError, bool shouldDrain = false,
+ bool forceUpdateError = false);
+
+ // Active/Idle/Suspended -> Stopped
+ StateChangeGuard stopOrUpdateError(QAudio::Error error = QAudio::NoError)
+ {
+ return stop(error, false, true);
+ }
+
+ // Stopped -> Active/Idle
+ StateChangeGuard start(bool isActive = true);
+
+ // Active/Idle -> Suspended + saves the exchanged state
+ StateChangeGuard suspend();
+
+ // Suspended -> saved state (Active/Idle)
+ StateChangeGuard resume();
+
+ // Idle -> Active
+ StateChangeGuard activateFromIdle();
+
+ // Active/Idle -> Active/Idle + updateError
+ StateChangeGuard updateActiveOrIdle(bool isActive, QAudio::Error error = QAudio::NoError);
+
+ // Any -> Any; better use more strict methods
+ StateChangeGuard forceSetState(QAudio::State state, QAudio::Error error = QAudio::NoError);
+
+ // force set the error
+ void setError(QAudio::Error error);
+
+private:
+ StateChangeGuard changeState(std::pair<RawState, uint32_t> prevStatesSet, RawState state,
+ QAudio::Error error = QAudio::NoError, bool shouldDrain = false);
+
+ void reset(RawState state, RawState prevState, QAudio::Error error);
+
+private:
+ QPointer<QAudioStateChangeNotifier> m_notifier;
+ std::atomic<RawState> m_state = QAudio::StoppedState;
+ std::atomic<QAudio::Error> m_error = QAudio::NoError;
+ RawState m_suspendedInState = QAudio::SuspendedState;
+
+ struct Synchronizer;
+ std::unique_ptr<Synchronizer> m_sychronizer;
+};
+
+QT_END_NAMESPACE
+
+#endif // QAUDIOSTATEMACHINE_P_H