MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
TapSet.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "Chain.hpp"
5
6namespace MayaFlux::Kriya {
7
8/**
9 * @class TapSet
10 * @brief One stream read by several independent taps.
11 *
12 * Like several samplers sharing a stream, each tap has its own entry time,
13 * speed, direction, level and output channels. Made by TapSetBuilder::start().
14 * Taps are numbered in the order they were declared. Copies share the same
15 * taps, so keep one alive for as long as the set should sound.
16 */
17class MAYAFLUX_API TapSet {
18public:
19 TapSet() = default;
20
21 /**
22 * @brief Number of taps.
23 */
24 [[nodiscard]] size_t tap_count() const { return m_taps.size(); }
25
26 /**
27 * @brief Change the level of a tap on all of its outputs.
28 * @param tap Tap number, in declaration order.
29 * @param gain Linear gain.
30 */
31 void level(size_t tap, double gain);
32
33 /**
34 * @brief Change the speed of a tap while it plays.
35 *
36 * The tap carries on from where it is, so the change never jumps, and all
37 * of its outputs change together. Zero holds the tap in place and a
38 * negative ratio reads backwards. It has no effect on a tap made with lag.
39 *
40 * @param tap Tap number, in declaration order.
41 * @param ratio Speed relative to the source.
42 */
43 void speed(size_t tap, double ratio);
44
45 /**
46 * @brief The slice behind one output of a tap, for looping and region edits.
47 *
48 * Changes take effect on the next block and apply to that output only. Use
49 * level() and speed() to change every output of a tap at once.
50 *
51 * @param tap Tap number, in declaration order.
52 * @param output Which of the tap's outputs, in the order its channels were given.
53 */
54 [[nodiscard]] Kakshya::StreamSlice& slice(size_t tap, size_t output = 0);
55
56 /**
57 * @brief The sampling pipeline that plays one output of a tap.
58 *
59 * Taps on the same output channel share one pipeline. Its buffer can be
60 * routed to another channel while the taps play.
61 *
62 * @param tap Tap number, in declaration order.
63 * @param output Which of the tap's outputs, in the order its channels were given.
64 */
65 [[nodiscard]] std::shared_ptr<SamplingPipeline> sampler(size_t tap, size_t output = 0) const;
66
67 /**
68 * @brief Silence every tap and cancel entries that have not happened yet.
69 */
70 void stop();
71
72private:
73 friend class TapSetBuilder;
74
75 struct Seat {
76 std::shared_ptr<SamplingPipeline> sampler;
77 size_t slot;
78 };
79
80 struct Tap {
81 std::vector<Seat> seats;
82 std::shared_ptr<double> velocity;
83 double rate;
84 };
85
86 std::vector<std::shared_ptr<SamplingPipeline>> m_pipelines;
87 std::vector<Tap> m_taps;
88 std::shared_ptr<EventChain> m_entries;
89};
90
91/**
92 * @class TapSetBuilder
93 * @brief Describes a tap set one tap at a time, then starts it.
94 *
95 * Each tap() begins a tap with defaults: enters at once, normal speed, forward,
96 * full level, output channel 0. The calls that follow describe that tap until
97 * the next tap(). Calling one before any tap() starts the first. loop() and
98 * region() apply to every tap.
99 *
100 * @code
101 * auto taps = MayaFlux::create_tap_set("res/h.wav")
102 * .tap().speed(1.0)
103 * .tap().enters_after(2.0).speed(3.0 / 2.0).level(0.8)
104 * .tap().enters_after(4.0).speed(2.0).backward().level(0.6).on_channels({ 0, 1 })
105 * .start();
106 *
107 * taps.speed(0, 0.5);
108 * taps.stop();
109 * @endcode
110 *
111 * Two taps at speeds 1.0 and 1.001 drift slowly out of phase. Speeds change
112 * pitch, since taps read with interpolation.
113 *
114 * A tap plays the source channel with the same number as each output channel,
115 * wrapping when the source has fewer, so a mono source sounds on every output
116 * and a stereo one keeps left and right. from_channel() picks one source
117 * channel for all of a tap's outputs.
118 */
119class MAYAFLUX_API TapSetBuilder {
120public:
121 /**
122 * @param stream Source stream. Null gives an empty set from start().
123 * @param mgr BufferManager the taps are supplied through.
124 * @param scheduler Scheduler for the pipelines and the entries.
125 * @param buf_size Engine buffer size in frames.
126 */
128 std::shared_ptr<Kakshya::DynamicSoundStream> stream,
130 Vruta::TaskScheduler& scheduler,
131 uint32_t buf_size);
132
133 /**
134 * @brief Begin a new tap.
135 */
136 TapSetBuilder& tap();
137
138 /**
139 * @brief Seconds after start() before the current tap enters.
140 */
141 TapSetBuilder& enters_after(double seconds);
142
143 /**
144 * @brief Speed of the current tap relative to the source. Negative also reads backwards.
145 */
146 TapSetBuilder& speed(double ratio);
147
148 /**
149 * @brief Read the current tap backwards from the end of the region.
150 */
151 TapSetBuilder& backward(bool enable = true);
152
153 /**
154 * @brief Linear gain of the current tap on each of its outputs.
155 */
156 TapSetBuilder& level(double gain);
157
158 /**
159 * @brief Output channel of the current tap.
160 */
161 TapSetBuilder& on_channel(uint32_t channel);
162
163 /**
164 * @brief Several output channels for the current tap, which stay in step.
165 *
166 * An empty list is ignored.
167 */
168 TapSetBuilder& on_channels(const std::vector<uint32_t>& channels);
169
170 /**
171 * @brief Source channel the current tap plays on every output.
172 */
173 TapSetBuilder& from_channel(uint32_t channel);
174
175 /**
176 * @brief Make the current tap trail the write head of a live ring by @p seconds.
177 *
178 * For a stream that is being filled through append_frames in circular mode,
179 * for example one made by create_ring and fed by record_into. The tap plays
180 * what was written that long ago. It replaces speed and direction, so speed()
181 * and repeat_every do not apply to it. Lags shorter than two blocks are
182 * raised to two blocks so the tap stays behind the frames being written.
183 */
184 TapSetBuilder& lag(double seconds);
185
186 /**
187 * @brief Make the current tap trail a live ring by a lag that follows a time map.
188 * @param seconds Map from seconds since the tap entered to the lag in seconds.
189 * A lag that grows plays slower and one that shrinks plays faster, so a
190 * moving lag glides or wobbles in pitch.
191 */
192 TapSetBuilder& lag(Kinesis::TimeMap seconds);
193
194 /**
195 * @brief Restart the current tap every @p seconds.
196 *
197 * Each repeat plays from the start of the region. Shorten it by giving a time
198 * map instead: it is read at the start of each repeat, so a shrinking length
199 * gives a stutter or bouncing repeat.
200 */
201 TapSetBuilder& repeat_every(double seconds);
202
203 /**
204 * @brief Restart the current tap with a repeat length that follows a time map.
205 * @param seconds Map from seconds since start() to the length of the repeat
206 * that begins then, in seconds, for example TimeMaps::exponential.
207 */
208 TapSetBuilder& repeat_every(Kinesis::TimeMap seconds);
209
210 /**
211 * @brief Whether taps loop the region (default) or play it once.
212 */
213 TapSetBuilder& loop(bool enable = true);
214
215 /**
216 * @brief Restrict every tap to a region of the stream.
217 * @param start_frame First frame.
218 * @param end_frame One past the last frame, 0 for the end of the stream.
219 */
220 TapSetBuilder& region(uint64_t start_frame, uint64_t end_frame = 0);
221
222 /**
223 * @brief Build the taps and start them.
224 * @return The running set, empty if the stream or region is unusable.
225 */
226 [[nodiscard]] TapSet start();
227
228private:
229 struct Spec {
230 double delay { 0.0 };
231 double ratio { 1.0 };
232 bool backward { false };
233 double level { 1.0 };
234 std::vector<uint32_t> channels { 0 };
235 std::optional<uint32_t> source;
236 std::shared_ptr<const Kinesis::TimeMap> repeat;
237 std::shared_ptr<const Kinesis::TimeMap> lag;
238 };
239
240 Spec& current();
241
242 std::shared_ptr<Kakshya::DynamicSoundStream> m_stream;
245 uint32_t m_buf_size;
246
247 std::vector<Spec> m_specs;
248 bool m_looping { true };
249 uint64_t m_start_frame {};
250 uint64_t m_end_frame {};
251};
252
253} // namespace MayaFlux::Kriya
Core::GlobalStreamInfo stream
Definition Config.cpp:40
glm::vec2 current
uint32_t channels
float gain
Token-based multimodal buffer management system for unified data stream processing.
std::shared_ptr< Kakshya::DynamicSoundStream > m_stream
Definition TapSet.hpp:242
std::vector< Spec > m_specs
Definition TapSet.hpp:247
Buffers::BufferManager & m_mgr
Definition TapSet.hpp:243
Vruta::TaskScheduler & m_scheduler
Definition TapSet.hpp:244
Describes a tap set one tap at a time, then starts it.
Definition TapSet.hpp:119
std::shared_ptr< EventChain > m_entries
Definition TapSet.hpp:88
std::vector< std::shared_ptr< SamplingPipeline > > m_pipelines
Definition TapSet.hpp:86
std::vector< Tap > m_taps
Definition TapSet.hpp:87
size_t tap_count() const
Number of taps.
Definition TapSet.hpp:24
One stream read by several independent taps.
Definition TapSet.hpp:17
Token-based multimodal task scheduling system for unified coroutine processing.
Definition Scheduler.hpp:51
A bounded region of a DynamicSoundStream with associated playback parameters.
Typed, composable, stateless callable from domain D to range R.
Definition Tendency.hpp:22
std::shared_ptr< const Kinesis::TimeMap > lag
Definition TapSet.hpp:237
std::shared_ptr< const Kinesis::TimeMap > repeat
Definition TapSet.hpp:236
std::optional< uint32_t > source
Definition TapSet.hpp:235
std::shared_ptr< SamplingPipeline > sampler
Definition TapSet.hpp:76
std::shared_ptr< double > velocity
Definition TapSet.hpp:82
std::vector< Seat > seats
Definition TapSet.hpp:81