MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
SoundStreamContainer.hpp
Go to the documentation of this file.
1#pragma once
2
4
6
7namespace MayaFlux::Kakshya {
8
9/**
10 * @class SoundStreamContainer
11 * @brief Concrete base implementation for streaming audio containers.
12 *
13 * SoundStreamContainer provides a complete, concrete implementation of all StreamContainer
14 * functionality for audio data. It serves as:
15 * 1. A standalone streaming container for real-time audio processing
16 * 2. A base class for specialized containers like SoundFileContainer
17 *
18 * The container implements all common audio streaming operations including:
19 * - Region management and processing state tracking
20 * - Sequential reading with looping support
21 * - Multi-dimensional data access and coordinate mapping
22 * - Processing chain integration and reader tracking
23 * - Memory layout optimization and data reorganization
24 *
25 * Uses virtual inheritance to support diamond inheritance when used as a base
26 * for FileContainer-derived classes.
27 */
28class MAYAFLUX_API SoundStreamContainer : public virtual StreamContainer {
29public:
30 /**
31 * @brief Construct a SoundStreamContainer with specified parameters.
32 * @param sample_rate Sample rate for temporal calculations
33 * @param num_channels Number of audio channels
34 * @param initial_capacity Initial capacity in frames (0 = minimal allocation)
35 * @param circular_mode If true, acts as circular buffer with fixed capacity
36 */
37 SoundStreamContainer(uint32_t sample_rate = 48000,
38 uint32_t num_channels = 2,
39 uint64_t initial_capacity = 0,
40 bool circular_mode = false);
41
42 ~SoundStreamContainer() override = default;
43
44 std::vector<DataDimension> get_dimensions() const override;
45 uint64_t get_total_elements() const override;
46 MemoryLayout get_memory_layout() const override { return m_structure.memory_layout; }
47 void set_memory_layout(MemoryLayout layout) override;
48
49 uint64_t get_frame_size() const override;
50 uint64_t get_num_frames() const override;
51
52 std::vector<DataVariant> get_region_data(const Region& region) const override;
53
54 /**
55 * @brief Copy arbitrary frames into a caller-owned buffer without allocating.
56 * @param frames Frame indices, in any order, repeats allowed.
57 * @param out Receives frames.size() * channel count values, frame-major.
58 * Indices past the end of the stream yield zeros.
59 */
60 void gather_frames(std::span<const uint64_t> frames, std::span<double> out) const;
61
62 void set_region_data(const Region& region, const std::vector<DataVariant>& data) override;
63
64 std::vector<DataVariant> get_region_group_data(const RegionGroup& group) const override;
65 std::vector<DataVariant> get_segments_data(const std::vector<RegionSegment>& segment) const override;
66
67 [[nodiscard]] std::type_index value_element_type() const override { return typeid(double); }
68
69 uint64_t coordinates_to_linear_index(const std::vector<uint64_t>& coordinates) const override;
70 std::vector<uint64_t> linear_index_to_coordinates(uint64_t linear_index) const override;
71
72 void clear() override;
73
74 const void* get_raw_data() const override;
75 bool has_data() const override;
76
77 inline ContainerDataStructure& get_structure() override { return m_structure; }
78 inline const ContainerDataStructure& get_structure() const override { return m_structure; }
79
80 inline void set_structure(ContainerDataStructure structure) override { m_structure = structure; }
81
82 void add_region_group(const RegionGroup& group) override;
83 RegionGroup get_region_group(const std::string& name) const override;
84 std::unordered_map<std::string, RegionGroup> get_all_region_groups() const override;
85 void remove_region_group(const std::string& name) override;
86
87 bool is_region_loaded(const Region& region) const override;
88 void load_region(const Region& region) override;
89 void unload_region(const Region& region) override;
90
91 void set_read_position(const std::vector<uint64_t>& position) override;
92 void update_read_position_for_channel(size_t channel, uint64_t frame) override;
93 const std::vector<uint64_t>& get_read_position() const override;
94 void advance_read_position(const std::vector<uint64_t>& frames) override;
95 bool is_at_end() const override;
96 void reset_read_position() override;
97
98 uint64_t get_temporal_rate() const override { return m_sample_rate; }
99 uint64_t time_to_position(double time) const override;
100 double position_to_time(uint64_t position) const override;
101
102 void set_looping(bool enable) override;
103 bool is_looping() const override { return m_looping_enabled; }
104 void set_loop_region(const Region& region) override;
105 Region get_loop_region() const override;
106
107 bool is_ready() const override;
108 std::vector<uint64_t> get_remaining_frames() const override;
109 uint64_t read_sequential(std::span<double> output, uint64_t count) override;
110 uint64_t peek_sequential(std::span<double> output, uint64_t count, uint64_t offset = 0) const override;
111
112 ProcessingState get_processing_state() const override { return m_processing_state.load(); }
113 void update_processing_state(ProcessingState new_state) override;
114
116 std::function<void(const std::shared_ptr<SignalSourceContainer>&, ProcessingState)> callback) override
117 {
118 Memory::SeqlockWriteGuard g(m_cb_lock);
119 m_state_callback = std::move(callback);
120 }
121
123 {
124 Memory::SeqlockWriteGuard g(m_cb_lock);
125 m_state_callback = nullptr;
126 }
127
128 bool is_ready_for_processing() const override;
129 void mark_ready_for_processing(bool ready) override;
130
131 void create_default_processor() override;
132 void process_default() override;
133
134 void set_default_processor(const std::shared_ptr<DataProcessor>& processor) override;
135 std::shared_ptr<DataProcessor> get_default_processor() const override;
136
137 std::shared_ptr<DataProcessingChain> get_processing_chain() override;
138 void set_processing_chain(const std::shared_ptr<DataProcessingChain>& chain) override { m_processing_chain = chain; }
139
140 uint32_t register_dimension_reader(uint32_t dimension_index) override;
141 void unregister_dimension_reader(uint32_t dimension_index) override;
142 bool has_active_readers() const override;
143 void mark_dimension_consumed(uint32_t dimension_index, uint32_t reader_id) override;
144 bool all_dimensions_consumed() const override;
145
146 virtual void clear_all_consumption();
147
148 inline std::vector<DataVariant>& get_processed_data() override
149 {
150 return m_processed_data;
151 }
152
153 inline const std::vector<DataVariant>& get_processed_data() const override
154 {
155 return m_processed_data;
156 }
157
158 void mark_buffers_for_processing(bool) override { /* Delegate to buffer integration */ }
159 void mark_buffers_for_removal() override { /* Delegate to buffer integration */ }
160
161 // void ensure_capacity(uint64_t required_frames);
162 // uint64_t get_capacity() const;
163 // void set_circular_mode(bool enable, std::optional<uint64_t> fixed_capacity = std::nullopt);
164 // bool is_circular_mode() const { return m_circular_mode; }
165
166 // void setup(uint64_t num_frames, uint32_t sample_rate, uint32_t num_channels);
167 // void set_raw_data(const DataVariant& data);
168 // void set_all_raw_data(const DataVariant& data);
169
170 virtual uint32_t get_sample_rate() const { return m_sample_rate; }
171
172 inline virtual uint32_t get_num_channels() const
173 {
174 return static_cast<uint32_t>(m_structure.get_channel_count());
175 }
176 // double get_duration_seconds() const;
177
178 inline void reset_processing_token() override { m_processing_token_channel.store(-1); }
179
180 inline bool try_acquire_processing_token(int channel) override
181 {
182 int expected = -1;
183 return m_processing_token_channel.compare_exchange_strong(expected, channel);
184 }
185
186 inline bool has_processing_token(int channel) const override
187 {
188 return m_processing_token_channel.load() == channel;
189 }
190
191 /**
192 * @brief Get the audio data as a specific type
193 * @return Span of double data for direct access
194 */
195 std::span<const double> get_data_as_double() const;
196
197 inline const std::vector<DataVariant>& get_data() override { return m_data; }
198
199 /**
200 * @brief Get channel data with semantic interpretation
201 * @param channel Channel index
202 * @return Type-erased data accessor
203 */
204 DataAccess channel_data(size_t channel) override;
205
206 /**
207 * @brief Get all channel data as accessors
208 */
209 std::vector<DataAccess> all_channel_data() override;
210
211protected:
212 void setup_dimensions();
213 void notify_state_change(ProcessingState new_state);
214 void reorganize_data_layout(MemoryLayout new_layout);
215
216 /** @brief Get the cached spans for each channel, recomputing if dirty */
217 const std::vector<std::span<double>>& get_span_cache() const;
218
219 /** @brief Invalidate the span cache when data or layout changes */
220 void invalidate_span_cache();
221
222 std::vector<DataVariant> m_data;
223 std::vector<DataVariant> m_processed_data;
224
225 std::atomic<int> m_processing_token_channel { -1 };
226
227 uint32_t m_sample_rate = 48000;
228 uint32_t m_num_channels {};
229 uint64_t m_num_frames {};
230
231 std::vector<std::atomic<uint64_t>> m_read_position;
232 bool m_looping_enabled = false;
234
235 bool m_circular_mode {};
236 uint64_t m_circular_write_position {};
237
238 std::atomic<ProcessingState> m_processing_state { ProcessingState::IDLE };
239 std::shared_ptr<DataProcessor> m_default_processor;
240 std::shared_ptr<DataProcessingChain> m_processing_chain;
241
242 std::unordered_map<std::string, RegionGroup> m_region_groups;
243
244 std::unordered_map<uint32_t, int> m_active_readers;
245 std::unordered_set<uint32_t> m_consumed_dimensions;
246
247 std::unordered_map<uint32_t, std::unordered_set<uint32_t>> m_reader_consumed_dimensions;
248 std::unordered_map<uint32_t, uint32_t> m_dimension_to_next_reader_id;
249
250 std::function<void(std::shared_ptr<SignalSourceContainer>, ProcessingState)> m_state_callback;
251
256
257 mutable std::vector<double> m_cached_ext_buffer;
258
259 mutable std::atomic<bool> m_double_extraction_dirty { true };
260 mutable std::mutex m_extraction_mutex;
261
263
264 auto get_frame_span_impl(uint64_t frame_index) const -> DataSpanVariant override
265 {
266 return get_frame_typed(frame_index);
267 }
268
269 void get_frames_impl(void* output, size_t count, uint64_t start_frame, uint64_t num_frames, const std::type_info& type) const override;
270
271 void get_value_impl(const std::vector<uint64_t>& coords,
272 void* out, const std::type_info& type) const override;
273
274 void set_value_impl(const std::vector<uint64_t>& coords,
275 const void* in, const std::type_info& type) override;
276
277private:
278 mutable std::optional<std::vector<std::span<double>>> m_span_cache;
279 mutable std::atomic<bool> m_span_cache_dirty { true };
280
281 std::span<const double> get_frame_typed(uint64_t frame_index) const;
282 void get_frames_typed(std::span<double> output, uint64_t start_frame, uint64_t num_frames) const;
283};
284
285} // namespace MayaFlux::Kakshya
uint64_t offset
Definition Composite.cpp:10
std::optional< glm::vec3 > position
Definition Decoder.cpp:93
BufferProcessingChain * chain
PickCallback callback
Definition Picker.cpp:22
std::vector< float > * out
std::vector< glm::ivec3 > coords
Definition VDBWriter.cpp:25
vk::PhysicalDeviceType type
Definition VKDevice.cpp:126
std::string name
Definition VKDevice.cpp:123
size_t count
Type-erased accessor for NDData with semantic view construction.
std::optional< std::vector< std::span< double > > > m_span_cache
std::unordered_map< std::string, RegionGroup > m_region_groups
std::unordered_map< uint32_t, int > m_active_readers
std::unordered_set< uint32_t > m_consumed_dimensions
const std::vector< DataVariant > & get_data() override
Get a reference to the raw data stored in the container.
const std::vector< DataVariant > & get_processed_data() const override
Get a const reference to the processed data buffer.
std::vector< DataVariant > & get_processed_data() override
Get a mutable reference to the processed data buffer.
std::vector< std::atomic< uint64_t > > m_read_position
void unregister_state_change_callback() override
Unregister the state change callback, if any.
std::shared_ptr< DataProcessingChain > m_processing_chain
void set_structure(ContainerDataStructure structure) override
Set the data structure for this container.
bool is_looping() const override
Check if looping is enabled for the stream.
auto get_frame_span_impl(uint64_t frame_index) const -> DataSpanVariant override
Implementation-specific method to retrieve a frame span.
void set_processing_chain(const std::shared_ptr< DataProcessingChain > &chain) override
Set the processing chain for this container.
const ContainerDataStructure & get_structure() const override
void mark_buffers_for_processing(bool) override
Mark associated buffers for processing in the next cycle.
MemoryLayout get_memory_layout() const override
Get the memory layout used by this container.
void register_state_change_callback(std::function< void(const std::shared_ptr< SignalSourceContainer > &, ProcessingState)> callback) override
Register a callback to be invoked on processing state changes.
ProcessingState get_processing_state() const override
Get the current processing state of the container.
bool try_acquire_processing_token(int channel) override
std::unordered_map< uint32_t, std::unordered_set< uint32_t > > m_reader_consumed_dimensions
std::shared_ptr< DataProcessor > m_default_processor
uint64_t get_temporal_rate() const override
Get the temporal rate (e.g., sample rate, frame rate) of the stream.
bool has_processing_token(int channel) const override
std::type_index value_element_type() const override
Runtime query for the native scalar element type of this container.
ContainerDataStructure & get_structure() override
Get the data structure defining this container's layout.
void mark_buffers_for_removal() override
Mark associated buffers for removal from the system.
std::function< void(std::shared_ptr< SignalSourceContainer >, ProcessingState)> m_state_callback
std::unordered_map< uint32_t, uint32_t > m_dimension_to_next_reader_id
Concrete base implementation for streaming audio containers.
Data-driven interface for temporal stream containers with navigable read position.
RAII guard that brackets a Seqlock write region.
Definition SeqLock.hpp:155
Sequence lock for fixed-size data regions.
Definition SeqLock.hpp:44
ProcessingState
Represents the current processing lifecycle state of a container.
void remove_region_group(std::unordered_map< std::string, RegionGroup > &groups, const std::string &name)
Remove a RegionGroup by name from a group map.
std::optional< RegionGroup > get_region_group(const std::unordered_map< std::string, RegionGroup > &groups, const std::string &name)
Get a RegionGroup by name from a group map.
typename detail::span_const_from_vector_variant< DataVariant >::type DataSpanVariant
Definition NDData.hpp:625
MemoryLayout
Memory layout for multi-dimensional data.
Definition NDData.hpp:33
uint64_t time_to_position(double time, double sample_rate)
Convert time (seconds) to position (samples/frames) given a sample rate.
double position_to_time(uint64_t position, double sample_rate)
Convert position (samples/frames) to time (seconds) given a sample rate.
void add_region_group(std::unordered_map< std::string, RegionGroup > &groups, const RegionGroup &group)
Add a RegionGroup to a group map.
Container structure for consistent dimension ordering.
Organizes related signal regions into a categorized collection.
Represents a point or span in N-dimensional space.
Definition Region.hpp:73