-
-
Notifications
You must be signed in to change notification settings - Fork 130
Expand file tree
/
Copy pathtransport.hpp
More file actions
162 lines (123 loc) · 5.18 KB
/
Copy pathtransport.hpp
File metadata and controls
162 lines (123 loc) · 5.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Copyright 2023 Kushview, LLC <info@kushview.net>
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <cstdint>
#include <element/atomic.hpp>
#include <element/shuttle.hpp>
namespace element {
/** Audio transport with thread-safe state management.
Extends Shuttle to provide a complete transport implementation with
lock-free communication between the audio thread and other threads.
State changes (play, record, tempo, seek) are requested from any thread
and applied during preProcess() on the audio thread. The Monitor class
provides read-only access to current transport state for UI updates.
@see Shuttle, Monitor
*/
class Transport : public Shuttle {
public:
/** Thread-safe, read-only view of transport state.
Monitor provides atomic access to transport parameters for UI display
and other non-audio threads. Values are updated by the Transport
during audio processing.
Reference counted for safe sharing between threads.
*/
class Monitor : public juce::ReferenceCountedObject {
public:
Monitor();
/** Number of beats per bar (time signature numerator). */
juce::Atomic<int> beatsPerBar;
/** Beat type/unit (time signature denominator, e.g. 4 = quarter note). */
juce::Atomic<int> beatType;
/** Beat divisor for subdivision calculations. */
juce::Atomic<int> beatDivisor;
/** Current sample rate in Hz. */
juce::Atomic<double> sampleRate;
/** Current tempo in beats per minute. */
juce::Atomic<float> tempo;
/** True if transport is currently playing. */
juce::Atomic<bool> playing;
/** True if transport is currently recording. */
juce::Atomic<bool> recording;
/** Current playhead position in audio frames (samples). */
juce::Atomic<int64_t> positionFrames;
/** Returns the ratio of beat type to beat divisor. */
double beatRatio() const noexcept;
/** Returns the current position in seconds. */
double getPositionSeconds() const;
/** Returns the current position in beats. */
float getPositionBeats() const;
/** Calculates bars, beats, and sub-beats from current position.
@param bars Output: bar number (0-based)
@param beats Output: beat within bar (0-based)
@param subBeats Output: sub-beat within beat (0-based)
@param subDivisions Number of sub-divisions per beat (default: 4)
*/
void getBarsAndBeats (int& bars, int& beats, int& subBeats, int subDivisions = 4);
};
using MonitorPtr = juce::ReferenceCountedObjectPtr<Monitor>;
Transport();
~Transport();
/** Returns beats per bar from the current time signature. */
int getBeatsPerBar() const { return getTimeScale().beatsPerBar(); }
/** Returns beat type (denominator) from the current time signature. */
int getBeatType() const { return getTimeScale().beatType(); }
/** Requests a play state change (thread-safe).
@param p True to play, false to stop
*/
inline void requestPlayState (bool p)
{
while (! playState.set (p)) {
}
}
/** Toggles between play and pause (thread-safe). */
inline void requestPlayPause() { requestPlayState (! playState.get()); }
/** Requests a record state change (thread-safe).
@param r True to record, false to stop recording
*/
inline void requestRecordState (bool r)
{
while (! recordState.set (r)) {
}
}
/** Requests a tempo change (thread-safe).
@param bpm New tempo in beats per minute
*/
inline void requestTempo (const double bpm)
{
while (! nextTempo.set (bpm)) {
}
}
/** Requests a time signature change (thread-safe).
@param beatsPerBar Numerator of time signature
@param beatType Beat type enum
*/
void requestMeter (int beatsPerBar, int beatType);
/** Requests a seek to a specific audio frame (thread-safe).
@param frame Target position in samples
*/
void requestAudioFrame (const int64_t frame);
/** Called at the start of each audio block to apply pending state changes.
@param nframes Number of samples in the upcoming block
*/
void preProcess (int nframes);
/** Called at the end of each audio block to update the monitor.
@param nframes Number of samples that were processed
*/
void postProcess (int nframes);
/** Returns the monitor for reading transport state from other threads. */
inline MonitorPtr getMonitor() const { return monitor; }
/** Applies any pending tempo change immediately.
@warning Must only be called from the audio thread. Calling from other
threads may cause race conditions. Safe to call when the audio engine
is not running.
*/
void applyTempo() noexcept;
private:
AtomicValue<bool> playState, recordState;
AtomicValue<double> nextTempo;
juce::Atomic<int> nextBeatsPerBar, nextBeatDivisor;
juce::Atomic<bool> seekWanted;
AtomicValue<int64_t> seekFrame;
MonitorPtr monitor;
};
} // namespace element