Zrythm v2.0.0-DEV
a highly automated and intuitive digital audio workstation
Loading...
Searching...
No Matches
recording_coordinator.h
1// SPDX-FileCopyrightText: © 2026 Alexandros Theodotou <alex@zrythm.org>
2// SPDX-License-Identifier: LicenseRef-ZrythmLicense
3
4#pragma once
5
6#include <atomic>
7#include <chrono>
8#include <memory>
9#include <unordered_map>
10#include <vector>
11
12#include "controllers/audio_recording_session.h"
13#include "structure/tracks/track_fwd.h"
14
15#include <QObject>
16
17namespace zrythm::controllers
18{
19
45class RecordingCoordinator : public QObject
46{
47 Q_OBJECT
48
49public:
50 static constexpr auto kDrainInterval = std::chrono::milliseconds (100);
51
52 explicit RecordingCoordinator (QObject * parent = nullptr);
53 ~RecordingCoordinator () override;
54
55 Q_DISABLE_COPY_MOVE (RecordingCoordinator)
56
57
62 void arm_track (
63 structure::tracks::TrackUuid track_id,
64 units::sample_u32_t max_block_length) [[clang::blocking]];
65
71 void disarm_track (structure::tracks::TrackUuid track_id) [[clang::blocking]];
72
78 [[nodiscard]] bool has_session (structure::tracks::TrackUuid track_id) const;
79
87
102 Q_SLOT void finalizeAllSessions ();
103
110 [[nodiscard]] AudioRecordingSession *
111 session_for_track (structure::tracks::TrackUuid track_id) const noexcept
112 [[clang::nonblocking]];
113
122 void prepare_for_processing (units::sample_u32_t block_length);
123
124Q_SIGNALS:
125
133 structure::tracks::TrackUuid track_id,
134 std::vector<RecordingAudioPacket> packets);
135
146
147private:
148 using Snapshot =
149 std::unordered_map<structure::tracks::TrackUuid, AudioRecordingSession *>;
150
151 using DrainResult = std::vector<
152 std::pair<structure::tracks::TrackUuid, std::vector<RecordingAudioPacket>>>;
153
154 void publish_snapshot ();
155
156 bool drain_pending_deletion (DrainResult &ready);
157
158 // TODO: Replace this raw atomic pointer with a proper multi-reader
159 // realtime-safe container. The current approach works but is ugly:
160 // publish_snapshot() heap-allocates a new Snapshot, atomically swaps the
161 // pointer, and defers the old Snapshot's deletion to
162 // pending_snapshot_deletion_. Those deferred snapshots are freed in
163 // process_pending(), which runs at least kDrainInterval (100 ms) after
164 // the last publish — long after any RT thread has finished reading the
165 // old snapshot. The initial snapshot (created in the constructor) and
166 // any remaining pending snapshots are freed in the destructor.
167 std::atomic<Snapshot *> rt_snapshot_{ nullptr };
168
169 // Two-phase deferred deletion for retired snapshots:
170 // 1. publish_snapshot() pushes the old snapshot into
171 // pending_snapshot_deletion_.
172 // 2. process_pending() swaps pending_snapshot_deletion_ into
173 // old_snapshots_ (destroying what was previously there) and emits
174 // signals. Each retired snapshot therefore survives at least two
175 // drain intervals (~200 ms), guaranteeing every RT reader has moved
176 // on to a newer snapshot before the old one is freed.
177 std::vector<std::unique_ptr<Snapshot>> pending_snapshot_deletion_;
178 std::vector<std::unique_ptr<Snapshot>> old_snapshots_;
179
180 struct Impl;
181 std::unique_ptr<Impl> impl_;
182};
183
184}
Per-track recording state with a lock-free SPSC queue.
void disarm_track(structure::tracks::TrackUuid track_id)
Disarms a track.
bool has_session(structure::tracks::TrackUuid track_id) const
Returns whether a session exists for the given track.
void arm_track(structure::tracks::TrackUuid track_id, units::sample_u32_t max_block_length)
Arms a track for recording.
AudioRecordingSession * session_for_track(structure::tracks::TrackUuid track_id) const noexcept
Gets the session for a track (for RT callback access).
Q_SLOT void finalizeAllSessions()
Finalizes the current recording take and resets sessions for reuse.
void prepare_for_processing(units::sample_u32_t block_length)
Prepares all active sessions for processing at the given block length.
void recordingSessionEnded()
Emitted when a recording take has been finalized.
void process_pending()
Drains all active sessions and processes pending deletions.
void audioDataReady(structure::tracks::TrackUuid track_id, std::vector< RecordingAudioPacket > packets)
Emitted when recorded audio data has been drained from a session.
A single packet of recorded audio data transferred from RT to non-RT.