MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
SoundStreamContainer.cpp
Go to the documentation of this file.
2
5
8
9namespace MayaFlux::Kakshya {
10
11SoundStreamContainer::SoundStreamContainer(uint32_t sample_rate, uint32_t num_channels,
12 uint64_t initial_capacity, bool circular_mode)
13 : m_sample_rate(sample_rate)
14 , m_num_channels(num_channels)
15 , m_num_frames(initial_capacity)
16 , m_circular_mode(circular_mode)
17{
18 m_processing_chain = std::make_shared<DataProcessingChain>();
20
21 m_read_position = std::vector<std::atomic<uint64_t>>(m_num_channels);
22 for (auto& pos : m_read_position) {
23 pos.store(0);
24 }
25
26 m_data = std::views::iota(0U, num_channels)
27 | std::views::transform([](auto) { return DataVariant(std::vector<double> {}); })
28 | std::ranges::to<std::vector>();
29}
30
32{
33 DataModality modality = (m_num_channels > 1)
36
37 std::vector<uint64_t> shape;
38 if (modality == DataModality::AUDIO_1D) {
39 shape = { m_num_frames };
40 } else {
41 shape = { m_num_frames, m_num_channels };
42 }
43
44 auto layout = m_structure.memory_layout;
46
47 m_structure = ContainerDataStructure(modality, org, layout);
49
52}
53
54std::vector<DataDimension> SoundStreamContainer::get_dimensions() const
55{
57}
58
63
73
75{
76 // return m_structure.get_channel_count();
77 return m_num_channels;
78}
79
81{
82 // return m_structure.get_samples_count_per_channel();
83 return m_num_frames;
84}
85
86std::vector<DataVariant> SoundStreamContainer::get_region_data(const Region& region) const
87{
88 const auto& spans = get_span_cache();
89
91 if (spans.empty())
92 return {};
93
94 std::span<const double> const_span(spans[0].data(), spans[0].size());
95 auto extracted = extract_region_data<double>(const_span, region, m_structure.dimensions);
96 return { DataVariant(std::move(extracted)) };
97 }
98
99 auto const_spans = spans | std::views::transform([](const auto& span) {
100 return std::span<const double>(span.data(), span.size());
101 });
102
103 auto extracted_channels = extract_region_data<double>(
104 std::vector<std::span<const double>>(const_spans.begin(), const_spans.end()),
105 region, m_structure.dimensions);
106
107 return extracted_channels
108 | std::views::transform([](auto&& channel) {
109 return DataVariant(std::forward<decltype(channel)>(channel));
110 })
111 | std::ranges::to<std::vector>();
112}
113
114void SoundStreamContainer::set_region_data(const Region& region, const std::vector<DataVariant>& data)
115{
117 if (m_data.empty() || data.empty())
118 return;
119
121 auto dest_span = convert_variant<double>(m_data[0]);
122 auto src_span = convert_variant<double>(data[0]);
123
124 set_or_update_region_data<double>(dest_span, src_span, region, m_structure.dimensions);
125 } else {
126 size_t channels_to_update = std::min(m_data.size(), data.size());
128
129 for (size_t i = 0; i < channels_to_update; ++i) {
130 auto dest_span = convert_variant<double>(m_data[i]);
131 auto src_span = convert_variant<double>(data[i]);
132 set_or_update_region_data<double>(dest_span, src_span, region, m_structure.dimensions);
133 }
134 }
135
137 m_double_extraction_dirty.store(true, std::memory_order_release);
138}
139
140std::vector<DataVariant> SoundStreamContainer::get_region_group_data(const RegionGroup& region_group) const
141{
142 const auto& spans = get_span_cache();
143
144 if (spans.empty())
145 return {};
146
147 auto const_spans = spans | std::views::transform([](const auto& span) {
148 return std::span<const double>(span.data(), span.size());
149 });
150
151 auto extracted_channels = extract_group_data<double>(
152 std::vector<std::span<const double>>(const_spans.begin(), const_spans.end()),
154
155 return extracted_channels
156 | std::views::transform([](auto&& channel) {
157 return DataVariant(std::forward<decltype(channel)>(channel));
158 })
159 | std::ranges::to<std::vector>();
160}
161
162std::vector<DataVariant> SoundStreamContainer::get_segments_data(const std::vector<RegionSegment>& segments) const
163{
164 const auto& spans = get_span_cache();
165
166 if (spans.empty() || segments.empty())
167 return {};
168
169 auto const_spans = spans | std::views::transform([](const auto& span) {
170 return std::span<const double>(span.data(), span.size());
171 });
172
173 auto extracted_channels = extract_segments_data<double>(
174 segments,
175 std::vector<std::span<const double>>(const_spans.begin(), const_spans.end()),
177
178 return extracted_channels
179 | std::views::transform([](auto&& channel) {
180 return DataVariant(std::forward<decltype(channel)>(channel));
181 })
182 | std::ranges::to<std::vector>();
183}
184
185void SoundStreamContainer::get_frames_impl(void* output, size_t count, uint64_t start_frame, uint64_t num_frames, const std::type_info& type) const
186{
187 if (type == typeid(double)) {
188 get_frames_typed(std::span<double>(static_cast<double*>(output), count), start_frame, num_frames);
189 return;
190 }
192 "SoundStreamContainer::get_frames_impl: unsupported type requested");
193}
194
195std::span<const double> SoundStreamContainer::get_frame_typed(uint64_t frame_index) const
196{
197 if (frame_index >= m_num_frames) {
198 return {};
199 }
200
201 const auto& spans = get_span_cache();
202
204 if (spans.empty())
205 return {};
206
207 auto frame_span = extract_frame<double>(spans[0], frame_index, m_num_channels);
208 return { frame_span.data(), frame_span.size() };
209 }
210
211 static thread_local std::vector<double> frame_buffer;
212 auto frame_span = extract_frame<double>(spans, frame_index, frame_buffer);
213 return { frame_span.data(), frame_span.size() };
214}
215
216void SoundStreamContainer::get_frames_typed(std::span<double> output, uint64_t start_frame, uint64_t num_frames) const
217{
218 if (start_frame >= m_num_frames || output.empty()) {
219 std::ranges::fill(output, 0.0);
220 return;
221 }
222
223 uint64_t frames_to_copy = std::min<size_t>(num_frames, m_num_frames - start_frame);
224 uint64_t elements_to_copy = std::min(
225 frames_to_copy * m_num_channels,
226 static_cast<uint64_t>(output.size()));
227
228 auto interleaved_data = get_data_as_double();
229 uint64_t offset = start_frame * m_num_channels;
230
231 if (offset < interleaved_data.size()) {
232 uint64_t available = std::min<size_t>(elements_to_copy, interleaved_data.size() - offset);
233 std::copy_n(interleaved_data.begin() + offset, available, output.begin());
234
235 if (available < output.size()) {
236 std::fill(output.begin() + available, output.end(), 0.0);
237 }
238 } else {
239 std::ranges::fill(output, 0.0);
240 }
241}
242
243uint64_t SoundStreamContainer::coordinates_to_linear_index(const std::vector<uint64_t>& coordinates) const
244{
245 return coordinates_to_linear(coordinates, m_structure.dimensions);
246}
247
248std::vector<uint64_t> SoundStreamContainer::linear_index_to_coordinates(uint64_t linear_index) const
249{
250 return linear_to_coordinates(linear_index, m_structure.dimensions);
251}
252
254{
255 {
257 std::ranges::for_each(m_data, [](auto& vec) {
258 std::visit([](auto& v) { v.clear(); }, vec);
259 });
260 std::ranges::for_each(m_processed_data, [](auto& vec) {
261 std::visit([](auto& v) { v.clear(); }, vec);
262 });
263 m_num_frames = 0;
265 m_read_position = std::vector<std::atomic<uint64_t>>(m_num_channels);
266 for (auto& pos : m_read_position)
267 pos.store(0);
269 }
270
273}
274
276{
277 auto span = get_data_as_double();
278 return span.empty() ? nullptr : span.data();
279}
280
282{
283 bool result = false;
284 seqlock_read_void(m_data_lock, 8, [&] {
285 result = std::ranges::any_of(m_data, [](const auto& variant) {
286 return std::visit([](const auto& vec) { return !vec.empty(); }, variant);
287 });
288 });
289 return result;
290}
291
297
299{
300 static const RegionGroup empty_group;
301
302 std::optional<RegionGroup> result;
303 seqlock_read_void(m_region_lock, 8, [&] {
304 auto it = m_region_groups.find(name);
305 result = (it != m_region_groups.end()) ? it->second : empty_group;
306 });
307 return result.value_or(empty_group);
308}
309
310std::unordered_map<std::string, RegionGroup> SoundStreamContainer::get_all_region_groups() const
311{
312 std::optional<std::unordered_map<std::string, RegionGroup>> result;
313
314 seqlock_read_void(m_region_lock, 8, [&] {
315 result = m_region_groups;
316 });
317 return result.value_or(std::unordered_map<std::string, RegionGroup> {});
318}
319
320void SoundStreamContainer::remove_region_group(const std::string& name)
321{
323 m_region_groups.erase(name);
324}
325
327{
328 return true;
329}
330
332{
333 // No-op for in-memory container
334}
335
337{
338 // No-op for in-memory container
339}
340
341void SoundStreamContainer::set_read_position(const std::vector<uint64_t>& position)
342{
343 if (m_read_position.size() != position.size()) {
344 m_read_position = std::vector<std::atomic<uint64_t>>(position.size());
345 }
346
347 auto wrapped_pos = wrap_position_with_loop(position, m_loop_region, m_looping_enabled);
348
349 for (size_t i = 0; i < wrapped_pos.size(); ++i) {
350 m_read_position[i].store(wrapped_pos[i]);
351 }
352}
353
355{
356 if (channel < m_read_position.size()) {
357 m_read_position[channel].store(frame);
358 }
359}
360
361const std::vector<uint64_t>& SoundStreamContainer::get_read_position() const
362{
363 static thread_local std::vector<uint64_t> pos_cache;
364 pos_cache.resize(m_read_position.size());
365
366 for (size_t i = 0; i < m_read_position.size(); ++i) {
367 pos_cache[i] = m_read_position[i].load();
368 }
369
370 return pos_cache;
371}
372
373void SoundStreamContainer::advance_read_position(const std::vector<uint64_t>& frames)
374{
375 if (frames.empty())
376 return;
377
378 std::vector<uint64_t> current_pos(m_read_position.size());
379 for (size_t i = 0; i < m_read_position.size(); ++i) {
380 current_pos[i] = m_read_position[i].load();
381 }
382
383 auto new_pos = advance_position(current_pos, frames, m_structure, m_looping_enabled, m_loop_region);
384
385 for (size_t i = 0; i < new_pos.size() && i < m_read_position.size(); ++i) {
386 m_read_position[i].store(new_pos[i]);
387 }
388}
389
391{
392 if (m_looping_enabled) {
393 return false;
394 }
395
396 if (m_read_position.empty()) {
397 return true;
398 }
399
400 uint64_t current_frame = m_read_position[0].load();
401 return current_frame >= m_num_frames;
402}
403
405{
406 std::vector<uint64_t> start_pos;
407
410 } else {
411 start_pos = std::vector<uint64_t>(m_num_channels, 0);
412 }
413
414 if (m_read_position.size() != start_pos.size()) {
415 m_read_position = std::vector<std::atomic<uint64_t>>(start_pos.size());
416 }
417
418 for (size_t i = 0; i < start_pos.size(); ++i) {
419 m_read_position[i].store(start_pos[i]);
420 }
421}
422
423uint64_t SoundStreamContainer::time_to_position(double time) const
424{
426}
427
428double SoundStreamContainer::position_to_time(uint64_t position) const
429{
431}
432
434{
435 m_looping_enabled = enable;
436 if (enable && m_loop_region.start_coordinates.empty()) {
438 }
439}
440
442{
443 m_loop_region = region;
444
445 if (m_looping_enabled && !region.start_coordinates.empty()) {
446 std::vector<uint64_t> current_pos(m_read_position.size());
447 for (size_t i = 0; i < m_read_position.size(); ++i) {
448 current_pos[i] = m_read_position[i].load();
449 }
450
451 bool outside_loop = false;
452 for (size_t i = 0; i < current_pos.size() && i < region.start_coordinates.size() && i < region.end_coordinates.size(); ++i) {
453 if (current_pos[i] < region.start_coordinates[i] || current_pos[i] > region.end_coordinates[i]) {
454 outside_loop = true;
455 break;
456 }
457 }
458
459 if (outside_loop) {
461 }
462 }
463}
464
469
471{
472 auto state = get_processing_state();
473 return has_data() && (state == ProcessingState::READY || state == ProcessingState::PROCESSED);
474}
475
477{
478 std::vector<uint64_t> frames(m_num_channels);
479 if (m_looping_enabled || m_read_position.empty()) {
480
481 for (auto& frame : frames) {
482 frame = std::numeric_limits<uint64_t>::max();
483 }
484 return frames;
485 }
486
487 for (size_t i = 0; i < frames.size(); i++) {
488 uint64_t current_frame = m_read_position[i].load();
489 frames[i] = (current_frame < m_num_frames) ? (m_num_frames - current_frame) : 0;
490 }
491 return frames;
492}
493
494uint64_t SoundStreamContainer::read_sequential(std::span<double> output, uint64_t count)
495{
496 uint64_t frames_read = peek_sequential(output, count, 0);
497
498 uint64_t frames_to_advance = frames_read / m_num_channels;
499 std::vector<uint64_t> advance_amount(m_num_channels, frames_to_advance);
500
501 advance_read_position(advance_amount);
502 return frames_read;
503}
504
505uint64_t SoundStreamContainer::peek_sequential(std::span<double> output, uint64_t count, uint64_t offset) const
506{
507 auto interleaved_span = get_data_as_double();
508 if (interleaved_span.empty() || output.empty())
509 return 0;
510
511 uint64_t start_frame = m_read_position.empty() ? 0 : m_read_position[0].load();
512 start_frame += offset;
513 uint64_t elements_to_read = std::min<uint64_t>(count, static_cast<uint64_t>(output.size()));
514
515 if (!m_looping_enabled) {
516 uint64_t linear_start = start_frame * m_num_channels;
517 if (linear_start >= interleaved_span.size()) {
518 std::ranges::fill(output, 0.0);
519 return 0;
520 }
521 auto view = interleaved_span
522 | std::views::drop(linear_start)
523 | std::views::take(elements_to_read);
524
525 auto copied = std::ranges::copy(view, output.begin());
526 std::ranges::fill(output.subspan(copied.out - output.begin()), 0.0);
527 return static_cast<uint64_t>(copied.out - output.begin());
528 }
529
530 if (m_loop_region.start_coordinates.empty()) {
531 std::ranges::fill(output, 0.0);
532 return 0;
533 }
534
535 uint64_t loop_start_frame = m_loop_region.start_coordinates[0];
536 uint64_t loop_end_frame = m_loop_region.end_coordinates[0];
537 uint64_t loop_length_frames = loop_end_frame - loop_start_frame + 1;
538
539 std::ranges::for_each(
540 std::views::iota(0UZ, elements_to_read),
541 [&](uint64_t i) {
542 uint64_t element_pos = start_frame * m_num_channels + i;
543 uint64_t frame_pos = element_pos / m_num_channels;
544 uint64_t channel_offset = element_pos % m_num_channels;
545
546 uint64_t wrapped_frame = ((frame_pos - loop_start_frame) % loop_length_frames) + loop_start_frame;
547 uint64_t wrapped_element = wrapped_frame * m_num_channels + channel_offset;
548
549 output[i] = (wrapped_element < interleaved_span.size()) ? interleaved_span[wrapped_element] : 0.0;
550 });
551
552 if (elements_to_read < output.size()) {
553 std::ranges::fill(output.subspan(elements_to_read), 0.0);
554 }
555 return elements_to_read;
556}
557
559{
560 ProcessingState old_state = m_processing_state.exchange(new_state);
561
562 if (old_state != new_state) {
563 notify_state_change(new_state);
564
565 if (new_state == ProcessingState::READY) {
567 m_consumed_dimensions.clear();
568 }
569 }
570}
571
573{
574 seqlock_read_void(m_cb_lock, 8, [&] {
576 m_state_callback(shared_from_this(), new_state);
577 });
578}
579
581{
582 auto state = get_processing_state();
583 return has_data() && (state == ProcessingState::READY || state == ProcessingState::PROCESSED);
584}
585
594
596{
597 auto processor = std::make_shared<ContiguousAccessProcessor>();
598 set_default_processor(processor);
599}
600
609
610void SoundStreamContainer::set_default_processor(const std::shared_ptr<DataProcessor>& processor)
611{
612 auto old_processor = m_default_processor;
613 m_default_processor = processor;
614
615 if (old_processor) {
616 old_processor->on_detach(shared_from_this());
617 }
618
619 if (processor) {
620 processor->on_attach(shared_from_this());
621 }
622}
623
624std::shared_ptr<DataProcessor> SoundStreamContainer::get_default_processor() const
625{
626 return m_default_processor;
627}
628
629std::shared_ptr<DataProcessingChain> SoundStreamContainer::get_processing_chain()
630{
631 if (!m_processing_chain) {
632 m_processing_chain = std::make_shared<DataProcessingChain>();
633 }
634 return m_processing_chain;
635}
636
637uint32_t SoundStreamContainer::register_dimension_reader(uint32_t dimension_index)
638{
640 m_active_readers[dimension_index]++;
641 uint32_t reader_id = m_dimension_to_next_reader_id[dimension_index]++;
642 m_reader_consumed_dimensions[reader_id] = std::unordered_set<uint32_t>();
643 return reader_id;
644}
645
647{
649 if (auto it = m_active_readers.find(dimension_index); it != m_active_readers.end()) {
650 auto& [dim, count] = *it;
651 if (--count <= 0) {
652 m_active_readers.erase(it);
653 m_dimension_to_next_reader_id.erase(dimension_index);
654 }
655 }
656}
657
659{
660 bool result = false;
661 seqlock_read_void(m_reader_lock, 8, [&] {
662 result = !m_active_readers.empty();
663 });
664 return result;
665}
666
667void SoundStreamContainer::mark_dimension_consumed(uint32_t dimension_index, uint32_t reader_id)
668{
670 if (m_reader_consumed_dimensions.contains(reader_id)) {
671 m_reader_consumed_dimensions[reader_id].insert(dimension_index);
672 } else {
674 "Attempted to mark dimension {} as consumed for unknown reader_id {}. "
675 "This may indicate the reader was not registered or has already been unregistered. "
676 "Please ensure readers are properly registered before marking dimensions as consumed.",
677 dimension_index, reader_id);
678 }
679}
680
682{
683 bool result = false;
684 seqlock_read_void(m_reader_lock, 8, [&] {
685 result = std::ranges::all_of(m_active_readers, [this](const auto& dim_reader_pair) {
686 const auto& [dim, expected_count] = dim_reader_pair;
687 auto actual_count = std::ranges::count_if(m_reader_consumed_dimensions,
688 [dim](const auto& reader_dims_pair) {
689 return reader_dims_pair.second.contains(dim);
690 });
691 return actual_count >= expected_count;
692 });
693 });
694 return result;
695}
696
698{
700 std::ranges::for_each(m_reader_consumed_dimensions,
701 [](auto& reader_dims_pair) {
702 reader_dims_pair.second.clear();
703 });
704}
705
707{
708 if (new_layout == m_structure.memory_layout) {
709 return;
710 }
711
713 m_structure.memory_layout = new_layout;
715 return;
716 }
717
719 m_structure.memory_layout = new_layout;
721 return;
722 }
723
724 auto current_span = convert_variant<double>(m_data[0]);
725 std::vector<double> current_data(current_span.begin(), current_span.end());
726
727 auto channels = deinterleave_channels<double>(
728 std::span<const double>(current_data.data(), current_data.size()),
730
731 std::vector<double> reorganized_data;
732 if (new_layout == MemoryLayout::ROW_MAJOR) {
733 reorganized_data = interleave_channels(channels);
734 } else {
735 reorganized_data.reserve(current_data.size());
736 for (const auto& channel : channels) {
737 reorganized_data.insert(reorganized_data.end(), channel.begin(), channel.end());
738 }
739 }
740
741 m_data[0] = DataVariant(std::move(reorganized_data));
742
744 m_double_extraction_dirty.store(true, std::memory_order_release);
745
746 m_structure.memory_layout = new_layout;
748}
749
750std::span<const double> SoundStreamContainer::get_data_as_double() const
751{
752 if (!m_double_extraction_dirty.load(std::memory_order_acquire))
753 return { m_cached_ext_buffer };
754
756 if (m_data.empty())
757 return {};
758
759 std::span<const double> result;
760 seqlock_read_void(m_data_lock, 8, [&] {
761 auto span = convert_variant<double>(m_data[0]);
762 result = { span.data(), span.size() };
763 });
764 return result;
765 }
766
767 const auto& spans = get_span_cache();
768
769 auto channels = spans
770 | std::views::transform([](const auto& span) {
771 return std::vector<double>(span.begin(), span.end());
772 })
773 | std::ranges::to<std::vector>();
774
776 m_double_extraction_dirty.store(false, std::memory_order_release);
777
778 return { m_cached_ext_buffer };
779}
780
782{
783 if (channel >= m_data.size()) {
784 error<std::out_of_range>(
787 std::source_location::current(),
788 "Channel index {} out of range (max {})",
789 channel, m_data.size() - 1);
790 }
792}
793
795{
796 std::vector<DataAccess> result;
797 result.reserve(m_data.size());
798
799 for (auto& i : m_data) {
800 result.emplace_back(i, m_structure.dimensions, m_structure.modality);
801 }
802 return result;
803}
804
805const std::vector<std::span<double>>& SoundStreamContainer::get_span_cache() const
806{
807 if (!m_span_cache_dirty.load(std::memory_order_acquire) && m_span_cache.has_value())
808 return *m_span_cache;
809
810 seqlock_read_void(m_data_lock, 8, [&] {
811 if (!m_span_cache_dirty.load(std::memory_order_acquire) && m_span_cache.has_value())
812 return;
813
814 auto spans = m_data
815 | std::views::transform([](auto& variant) {
816 return convert_variant<double>(const_cast<DataVariant&>(variant));
817 })
818 | std::ranges::to<std::vector>();
819
820 m_span_cache = std::move(spans);
821 m_span_cache_dirty.store(false, std::memory_order_release);
822 });
823
824 return *m_span_cache;
825}
826
828{
829 m_span_cache_dirty.store(true, std::memory_order_release);
830}
831
833 const std::vector<uint64_t>& coords,
834 void* out,
835 const std::type_info& type) const
836{
837 if (type != typeid(double) || coords.size() != 2 || !has_data())
838 return;
839
840 const uint64_t frame = coords[0];
841 const uint64_t channel = coords[1];
842
843 if (frame >= m_num_frames || channel >= m_num_channels)
844 return;
845
846 const auto& spans = get_span_cache();
847
849 if (spans.empty())
850 return;
851 const uint64_t idx = frame * m_num_channels + channel;
852 if (idx >= spans[0].size())
853 return;
854 *static_cast<double*>(out) = spans[0][idx];
855 } else {
856 if (channel >= spans.size() || frame >= spans[channel].size())
857 return;
858 *static_cast<double*>(out) = spans[channel][frame];
859 }
860}
861
863 const std::vector<uint64_t>& coords,
864 const void* in,
865 const std::type_info& type)
866{
867 if (type != typeid(double) || coords.size() != 2)
868 return;
869
870 const uint64_t frame = coords[0];
871 const uint64_t channel = coords[1];
872
873 if (frame >= m_num_frames || channel >= m_num_channels)
874 return;
875
876 const auto& spans = get_span_cache();
877
879 if (spans.empty())
880 return;
881 const uint64_t idx = frame * m_num_channels + channel;
882 if (idx >= spans[0].size())
883 return;
884 const_cast<std::span<double>&>(spans[0])[idx] = *static_cast<const double*>(in);
885 } else {
886 if (channel >= spans.size() || frame >= spans[channel].size())
887 return;
888 const_cast<std::span<double>&>(spans[channel])[frame] = *static_cast<const double*>(in);
889 }
890
892 m_double_extraction_dirty.store(true, std::memory_order_release);
893}
894
895}
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
size_t count
std::shared_ptr< Core::VKImage > output
float offset
Type-erased accessor for NDData with semantic view construction.
std::span< const double > get_frame_typed(uint64_t frame_index) const
void reset_read_position() override
Reset read position to the beginning of the stream.
std::atomic< ProcessingState > m_processing_state
std::span< const double > get_data_as_double() const
Get the audio data as a specific type.
void get_frames_typed(std::span< double > output, uint64_t start_frame, uint64_t num_frames) const
std::optional< std::vector< std::span< double > > > m_span_cache
void set_memory_layout(MemoryLayout layout) override
Set the memory layout for this container.
std::unordered_map< std::string, RegionGroup > m_region_groups
double position_to_time(uint64_t position) const override
Convert from position units (e.g., frame/sample index) to time (seconds).
Region get_loop_region() const override
Get the current loop region.
std::unordered_map< uint32_t, int > m_active_readers
void unload_region(const Region &region) override
Unload a region from memory.
std::unordered_set< uint32_t > m_consumed_dimensions
uint32_t register_dimension_reader(uint32_t dimension_index) override
Register a reader for a specific dimension.
bool has_active_readers() const override
Check if any dimensions currently have active readers.
void set_looping(bool enable) override
Enable or disable looping behavior for the stream.
uint64_t get_total_elements() const override
Get the total number of elements in the container.
std::vector< std::atomic< uint64_t > > m_read_position
const std::vector< std::span< double > > & get_span_cache() const
Get the cached spans for each channel, recomputing if dirty.
bool is_region_loaded(const Region &region) const override
Check if a region is loaded in memory.
std::shared_ptr< DataProcessingChain > m_processing_chain
bool all_dimensions_consumed() const override
Check if all active dimensions have been consumed in this cycle.
uint64_t peek_sequential(std::span< double > output, uint64_t count, uint64_t offset=0) const override
Peek at data without advancing the read position.
std::vector< DataVariant > get_region_data(const Region &region) const override
Get data for a specific region.
void get_value_impl(const std::vector< uint64_t > &coords, void *out, const std::type_info &type) const override
Type-erased single-element read.
uint64_t time_to_position(double time) const override
Convert from time (seconds) to position units (e.g., frame/sample index).
const void * get_raw_data() const override
Get a raw pointer to the underlying data storage.
void advance_read_position(const std::vector< uint64_t > &frames) override
Advance the read position by a specified amount.
void unregister_dimension_reader(uint32_t dimension_index) override
Unregister a reader for a specific dimension.
void reorganize_data_layout(MemoryLayout new_layout)
void update_processing_state(ProcessingState new_state) override
Update the processing state of the container.
void mark_ready_for_processing(bool ready) override
Mark the container as ready or not ready for processing.
void set_value_impl(const std::vector< uint64_t > &coords, const void *in, const std::type_info &type) override
Type-erased single-element write.
void set_read_position(const std::vector< uint64_t > &position) override
Set the current read position in the primary temporal dimension per channel.
std::vector< uint64_t > linear_index_to_coordinates(uint64_t linear_index) const override
Convert linear index to coordinates based on current memory layout.
void notify_state_change(ProcessingState new_state)
bool has_data() const override
Check if the container currently holds any data.
const std::vector< uint64_t > & get_read_position() const override
Get the current read position.
void set_default_processor(const std::shared_ptr< DataProcessor > &processor) override
Set the default data processor for this container.
void load_region(const Region &region) override
Load a region into memory.
void add_region_group(const RegionGroup &group) override
Add a named group of regions to the container.
ProcessingState get_processing_state() const override
Get the current processing state of the container.
void create_default_processor() override
Create and configure a default processor for this container.
void invalidate_span_cache()
Invalidate the span cache when data or layout changes.
bool is_ready_for_processing() const override
Check if the container is ready for processing.
std::shared_ptr< DataProcessingChain > get_processing_chain() override
Get the current processing chain for this container.
uint64_t read_sequential(std::span< double > output, uint64_t count) override
Read data sequentially from the current position.
DataAccess channel_data(size_t channel) override
Get channel data with semantic interpretation.
std::vector< DataDimension > get_dimensions() const override
Get the dimensions describing the structure of the data.
std::unordered_map< std::string, RegionGroup > get_all_region_groups() const override
Get all region groups in the container.
bool is_at_end() const override
Check if read position has reached the end of the stream.
std::vector< DataAccess > all_channel_data() override
Get all channel data as accessors.
std::vector< DataVariant > get_segments_data(const std::vector< RegionSegment > &segment) const override
Get data for multiple region segments efficiently.
uint64_t get_num_frames() const override
Get the number of frames in the primary (temporal) dimension.
std::unordered_map< uint32_t, std::unordered_set< uint32_t > > m_reader_consumed_dimensions
std::shared_ptr< DataProcessor > m_default_processor
void remove_region_group(const std::string &name) override
Remove a region group by name.
std::vector< DataVariant > get_region_group_data(const RegionGroup &group) const override
Get data for multiple regions efficiently.
void get_frames_impl(void *output, size_t count, uint64_t start_frame, uint64_t num_frames, const std::type_info &type) const override
Implementation-specific method to retrieve multiple frames.
void clear() override
Clear all data in the container.
void process_default() override
Process the container's data using the default processor.
void set_region_data(const Region &region, const std::vector< DataVariant > &data) override
Set data for a specific region.
void update_read_position_for_channel(size_t channel, uint64_t frame) override
Update the read position for a specific channel.
uint64_t coordinates_to_linear_index(const std::vector< uint64_t > &coordinates) const override
Convert coordinates to linear index based on current memory layout.
SoundStreamContainer(uint32_t sample_rate=48000, uint32_t num_channels=2, uint64_t initial_capacity=0, bool circular_mode=false)
Construct a SoundStreamContainer with specified parameters.
void set_loop_region(const Region &region) override
Set the loop region using a Region.
std::vector< uint64_t > get_remaining_frames() const override
Get the number of remaining frames from the current position, per channel.
void mark_dimension_consumed(uint32_t dimension_index, uint32_t reader_id) override
Mark a dimension as consumed for the current processing cycle.
std::function< void(std::shared_ptr< SignalSourceContainer >, ProcessingState)> m_state_callback
bool is_ready() const override
Check if the stream is ready for reading.
std::unordered_map< uint32_t, uint32_t > m_dimension_to_next_reader_id
std::shared_ptr< DataProcessor > get_default_processor() const override
Get the current default data processor.
RegionGroup get_region_group(const std::string &name) const override
Get a region group by name.
uint64_t get_frame_size() const override
Get the number of elements that constitute one "frame".
RAII guard that brackets a Seqlock write region.
Definition SeqLock.hpp:136
@ ContainerProcessing
Container operations (Kakshya - file/stream/region processing)
@ Runtime
General runtime operations (default fallback)
@ Kakshya
Containers[Signalsource, Stream, File], Regions, DataProcessors.
ProcessingState
Represents the current processing lifecycle state of a container.
@ READY
Container has data loaded and is ready for processing.
@ IDLE
Container is inactive with no data or not ready for processing.
@ PROCESSING
Container is actively being processed.
@ PROCESSED
Container has completed processing and results are available.
uint64_t coordinates_to_linear(const std::vector< uint64_t > &coords, const std::vector< DataDimension > &dimensions)
Convert N-dimensional coordinates to a linear index for interleaved data.
Definition CoordUtils.cpp:8
std::variant< std::vector< double >, std::vector< float >, std::vector< uint8_t >, std::vector< uint16_t >, std::vector< uint32_t >, std::vector< std::complex< float > >, std::vector< std::complex< double > >, std::vector< glm::vec2 >, std::vector< glm::vec3 >, std::vector< glm::vec4 >, std::vector< glm::mat4 > > DataVariant
Multi-type data storage for different precision needs.
Definition NDData.hpp:102
DataModality
Data modality types for cross-modal analysis.
Definition NDData.hpp:164
@ AUDIO_MULTICHANNEL
Multi-channel audio.
std::vector< uint64_t > advance_position(const std::vector< uint64_t > &current_positions, uint64_t frames_to_advance, const ContainerDataStructure &structure, bool looping_enabled, const Region &loop_region)
Advance current positions by a number of frames, with optional looping.
std::vector< uint64_t > linear_to_coordinates(uint64_t index, const std::vector< DataDimension > &dimensions)
Convert a linear index to N-dimensional coordinates for interleaved data.
MemoryLayout
Memory layout for multi-dimensional data.
Definition NDData.hpp:65
@ ROW_MAJOR
C/C++ style (last dimension varies fastest)
double position_to_time(uint64_t position, double sample_rate)
Convert position (samples/frames) to time (seconds) given a sample rate.
OrganizationStrategy
Data organization strategy for multi-channel/multi-frame data.
Definition NDData.hpp:75
@ PLANAR
Separate DataVariant per logical unit (LLL...RRR for stereo)
@ INTERLEAVED
Single DataVariant with interleaved data (LRLRLR for stereo)
std::vector< T > interleave_channels(const std::vector< std::vector< T > > &channels)
Interleave multiple channels of data into a single vector.
uint64_t time_to_position(double time, double sample_rate)
Convert time (seconds) to position (samples/frames) given a sample rate.
std::vector< uint64_t > wrap_position_with_loop(const std::vector< uint64_t > &positions, const Region &loop_region, bool looping_enabled)
Wrap a position within loop boundaries if looping is enabled.
static uint64_t get_total_elements(const std::vector< DataDimension > &dimensions)
Get total elements across all dimensions.
Container structure for consistent dimension ordering.
static std::vector< DataDimension > create_dimensions(DataModality modality, const std::vector< uint64_t > &shape, MemoryLayout layout=MemoryLayout::ROW_MAJOR)
Create dimension descriptors for a data modality.
Definition NDData.cpp:136
std::string name
Descriptive name of the group.
Organizes related signal regions into a categorized collection.
static Region time_span(uint64_t start_frame, uint64_t end_frame, const std::string &label="", const std::any &extra_data={})
Create a Region representing a time span (e.g., a segment of frames).
Definition Region.hpp:141
std::vector< uint64_t > end_coordinates
Ending frame index (inclusive)
Definition Region.hpp:78
std::vector< uint64_t > start_coordinates
Starting frame index (inclusive)
Definition Region.hpp:75
Represents a point or span in N-dimensional space.
Definition Region.hpp:73