MayaFlux 0.1.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
SoundStreamContainer.cpp
Go to the documentation of this file.
2
4
7
8namespace MayaFlux::Kakshya {
9
10SoundStreamContainer::SoundStreamContainer(uint32_t sample_rate, uint32_t num_channels,
11 uint64_t initial_capacity, bool circular_mode)
12 : m_sample_rate(sample_rate)
13 , m_num_channels(num_channels)
14 , m_num_frames(initial_capacity)
15 , m_circular_mode(circular_mode)
16{
18
19 m_read_position = std::vector<std::atomic<uint64_t>>(m_num_channels);
20 for (auto& pos : m_read_position) {
21 pos.store(0);
22 }
23
24 m_data = std::views::iota(0U, num_channels)
25 | std::views::transform([](auto) { return DataVariant(std::vector<double> {}); })
26 | std::ranges::to<std::vector>();
27}
28
30{
31 DataModality modality = (m_num_channels > 1)
34
35 std::vector<uint64_t> shape;
36 if (modality == DataModality::AUDIO_1D) {
37 shape = { m_num_frames };
38 } else {
39 shape = { m_num_frames, m_num_channels };
40 }
41
42 auto layout = m_structure.memory_layout;
44
45 m_structure = ContainerDataStructure(modality, org, layout);
47
50}
51
52std::vector<DataDimension> SoundStreamContainer::get_dimensions() const
53{
55}
56
61
63{
64 if (layout != m_structure.memory_layout) {
65 std::unique_lock lock(m_data_mutex);
69 }
70}
71
73{
74 // return m_structure.get_channel_count();
75 return m_num_channels;
76}
77
79{
80 // return m_structure.get_samples_count_per_channel();
81 return m_num_frames;
82}
83
84std::vector<DataVariant> SoundStreamContainer::get_region_data(const Region& region) const
85{
86 const auto& spans = get_span_cache();
87
89 if (spans.empty())
90 return {};
91
92 std::span<const double> const_span(spans[0].data(), spans[0].size());
93 auto extracted = extract_region_data<double>(const_span, region, m_structure.dimensions);
94 return { DataVariant(std::move(extracted)) };
95 }
96
97 auto const_spans = spans | std::views::transform([](const auto& span) {
98 return std::span<const double>(span.data(), span.size());
99 });
100
101 auto extracted_channels = extract_region_data<double>(
102 std::vector<std::span<const double>>(const_spans.begin(), const_spans.end()),
103 region, m_structure.dimensions);
104
105 return extracted_channels
106 | std::views::transform([](auto&& channel) {
107 return DataVariant(std::forward<decltype(channel)>(channel));
108 })
109 | std::ranges::to<std::vector>();
110}
111
112void SoundStreamContainer::set_region_data(const Region& region, const std::vector<DataVariant>& data)
113{
115 if (m_data.empty() || data.empty())
116 return;
117
118 std::unique_lock lock(m_data_mutex);
119
120 auto dest_span = convert_variant<double>(m_data[0]);
121 auto src_span = convert_variant<double>(data[0]);
122
123 set_or_update_region_data<double>(
124 dest_span, src_span, region, m_structure.dimensions);
125
126 } else {
127 size_t channels_to_update = std::min(m_data.size(), data.size());
128
129 std::unique_lock lock(m_data_mutex);
130 for (size_t i = 0; i < channels_to_update; ++i) {
131 auto dest_span = convert_variant<double>(m_data[i]);
132 auto src_span = convert_variant<double>(data[i]);
133
134 set_or_update_region_data<double>(
135 dest_span, src_span, region, m_structure.dimensions);
136 }
137 }
138
140 m_double_extraction_dirty.store(true, std::memory_order_release);
141}
142
143std::vector<DataVariant> SoundStreamContainer::get_region_group_data(const RegionGroup& region_group) const
144{
145 const auto& spans = get_span_cache();
146
147 if (spans.empty())
148 return {};
149
150 auto const_spans = spans | std::views::transform([](const auto& span) {
151 return std::span<const double>(span.data(), span.size());
152 });
153
154 auto extracted_channels = extract_group_data<double>(
155 std::vector<std::span<const double>>(const_spans.begin(), const_spans.end()),
157
158 return extracted_channels
159 | std::views::transform([](auto&& channel) {
160 return DataVariant(std::forward<decltype(channel)>(channel));
161 })
162 | std::ranges::to<std::vector>();
163}
164
165std::vector<DataVariant> SoundStreamContainer::get_segments_data(const std::vector<RegionSegment>& segments) const
166{
167 const auto& spans = get_span_cache();
168
169 if (spans.empty() || segments.empty())
170 return {};
171
172 auto const_spans = spans | std::views::transform([](const auto& span) {
173 return std::span<const double>(span.data(), span.size());
174 });
175
176 auto extracted_channels = extract_segments_data<double>(
177 segments,
178 std::vector<std::span<const double>>(const_spans.begin(), const_spans.end()),
180
181 return extracted_channels
182 | std::views::transform([](auto&& channel) {
183 return DataVariant(std::forward<decltype(channel)>(channel));
184 })
185 | std::ranges::to<std::vector>();
186}
187
188std::span<const double> SoundStreamContainer::get_frame(uint64_t frame_index) const
189{
190 if (frame_index >= m_num_frames) {
191 return {};
192 }
193
194 const auto& spans = get_span_cache();
195
197 if (spans.empty())
198 return {};
199
200 auto frame_span = extract_frame<double>(spans[0], frame_index, m_num_channels);
201 return { frame_span.data(), frame_span.size() };
202 }
203
204 static thread_local std::vector<double> frame_buffer;
205 auto frame_span = extract_frame<double>(spans, frame_index, frame_buffer);
206 return { frame_span.data(), frame_span.size() };
207}
208
209void SoundStreamContainer::get_frames(std::span<double> output, uint64_t start_frame, uint64_t num_frames) const
210{
211 if (start_frame >= m_num_frames || output.empty()) {
212 std::ranges::fill(output, 0.0);
213 return;
214 }
215
216 uint64_t frames_to_copy = std::min<size_t>(num_frames, m_num_frames - start_frame);
217 uint64_t elements_to_copy = std::min(
218 frames_to_copy * m_num_channels,
219 static_cast<uint64_t>(output.size()));
220
221 auto interleaved_data = get_data_as_double();
222 uint64_t offset = start_frame * m_num_channels;
223
224 if (offset < interleaved_data.size()) {
225 uint64_t available = std::min<size_t>(elements_to_copy, interleaved_data.size() - offset);
226 std::copy_n(interleaved_data.begin() + offset, available, output.begin());
227
228 if (available < output.size()) {
229 std::fill(output.begin() + available, output.end(), 0.0);
230 }
231 } else {
232 std::ranges::fill(output, 0.0);
233 }
234}
235
236double SoundStreamContainer::get_value_at(const std::vector<uint64_t>& coordinates) const
237{
238 if (!has_data() || coordinates.size() != 2) {
239 return 0.0;
240 }
241
242 uint64_t frame = coordinates[0];
243 uint64_t channel = coordinates[1];
244
245 if (frame >= m_num_frames || channel >= m_num_channels) {
246 return 0.0;
247 }
248
249 const auto& spans = get_span_cache();
250
252 if (spans.empty())
253 return 0.0;
254
255 uint64_t linear_index = frame * m_num_channels + channel;
256 return (linear_index < spans[0].size()) ? spans[0][linear_index] : 0.0;
257 }
258
259 if (channel >= spans.size())
260 return 0.0;
261
262 return (frame < spans[channel].size()) ? spans[channel][frame] : 0.0;
263}
264
265void SoundStreamContainer::set_value_at(const std::vector<uint64_t>& coordinates, double value)
266{
267 if (coordinates.size() != 2)
268 return;
269
270 uint64_t frame = coordinates[0];
271 uint64_t channel = coordinates[1];
272
273 if (frame >= m_num_frames || channel >= m_num_channels) {
274 return;
275 }
276
277 const auto& spans = get_span_cache();
278
280 if (spans.empty())
281 return;
282
283 uint64_t linear_index = frame * m_num_channels + channel;
284 if (linear_index < spans[0].size()) {
285 const_cast<std::span<double>&>(spans[0])[linear_index] = value;
286 }
287
288 } else {
289 if (channel >= spans.size())
290 return;
291
292 if (frame < spans[channel].size()) {
293 const_cast<std::span<double>&>(spans[channel])[frame] = value;
294 }
295 }
296
298 m_double_extraction_dirty.store(true, std::memory_order_release);
299}
300
301uint64_t SoundStreamContainer::coordinates_to_linear_index(const std::vector<uint64_t>& coordinates) const
302{
303 return coordinates_to_linear(coordinates, m_structure.dimensions);
304}
305
306std::vector<uint64_t> SoundStreamContainer::linear_index_to_coordinates(uint64_t linear_index) const
307{
308 return linear_to_coordinates(linear_index, m_structure.dimensions);
309}
310
312{
313 std::unique_lock lock(m_data_mutex);
314
315 std::ranges::for_each(m_data, [](auto& vec) {
316 std::visit([](auto& v) { v.clear(); }, vec);
317 });
318
319 std::ranges::for_each(m_processed_data, [](auto& vec) {
320 std::visit([](auto& v) { v.clear(); }, vec);
321 });
322
323 m_num_frames = 0;
325
326 m_read_position = std::vector<std::atomic<uint64_t>>(m_num_channels);
327 for (auto& pos : m_read_position) {
328 pos.store(0);
329 }
330
333}
334
336{
337 auto span = get_data_as_double();
338 return span.empty() ? nullptr : span.data();
339}
340
342{
343 std::shared_lock lock(m_data_mutex);
344
345 return std::ranges::any_of(m_data, [](const auto& variant) {
346 return std::visit([](const auto& vec) { return !vec.empty(); }, variant);
347 });
348}
349
351{
352 std::lock_guard<std::mutex> lock(m_state_mutex);
353 m_region_groups[group.name] = group;
354}
355
356const RegionGroup& SoundStreamContainer::get_region_group(const std::string& name) const
357{
358 static const RegionGroup empty_group;
359
360 std::shared_lock lock(m_data_mutex);
361 auto it = m_region_groups.find(name);
362 return it != m_region_groups.end() ? it->second : empty_group;
363}
364
365std::unordered_map<std::string, RegionGroup> SoundStreamContainer::get_all_region_groups() const
366{
367 std::lock_guard<std::mutex> lock(m_state_mutex);
368 return m_region_groups;
369}
370
371void SoundStreamContainer::remove_region_group(const std::string& name)
372{
373 std::lock_guard<std::mutex> lock(m_state_mutex);
374 m_region_groups.erase(name);
375}
376
378{
379 return true;
380}
381
383{
384 // No-op for in-memory container
385}
386
388{
389 // No-op for in-memory container
390}
391
392void SoundStreamContainer::set_read_position(const std::vector<uint64_t>& position)
393{
394 if (m_read_position.size() != position.size()) {
395 m_read_position = std::vector<std::atomic<uint64_t>>(position.size());
396 }
397
398 auto wrapped_pos = wrap_position_with_loop(position, m_loop_region, m_looping_enabled);
399
400 for (size_t i = 0; i < wrapped_pos.size(); ++i) {
401 m_read_position[i].store(wrapped_pos[i]);
402 }
403}
404
406{
407 if (channel < m_read_position.size()) {
408 m_read_position[channel].store(frame);
409 }
410}
411
412const std::vector<uint64_t>& SoundStreamContainer::get_read_position() const
413{
414 static thread_local std::vector<uint64_t> pos_cache;
415 pos_cache.resize(m_read_position.size());
416
417 for (size_t i = 0; i < m_read_position.size(); ++i) {
418 pos_cache[i] = m_read_position[i].load();
419 }
420
421 return pos_cache;
422}
423
424void SoundStreamContainer::advance_read_position(const std::vector<uint64_t>& frames)
425{
426 if (frames.empty())
427 return;
428
429 std::vector<uint64_t> current_pos(m_read_position.size());
430 for (size_t i = 0; i < m_read_position.size(); ++i) {
431 current_pos[i] = m_read_position[i].load();
432 }
433
434 auto new_pos = advance_position(current_pos, frames, m_structure, m_looping_enabled, m_loop_region);
435
436 for (size_t i = 0; i < new_pos.size() && i < m_read_position.size(); ++i) {
437 m_read_position[i].store(new_pos[i]);
438 }
439}
440
442{
443 if (m_looping_enabled) {
444 return false;
445 }
446
447 if (m_read_position.empty()) {
448 return true;
449 }
450
451 uint64_t current_frame = m_read_position[0].load();
452 return current_frame >= m_num_frames;
453}
454
456{
457 std::vector<uint64_t> start_pos;
458
461 } else {
462 start_pos = std::vector<uint64_t>(m_num_channels, 0);
463 }
464
465 if (m_read_position.size() != start_pos.size()) {
466 m_read_position = std::vector<std::atomic<uint64_t>>(start_pos.size());
467 }
468
469 for (size_t i = 0; i < start_pos.size(); ++i) {
470 m_read_position[i].store(start_pos[i]);
471 }
472}
473
474uint64_t SoundStreamContainer::time_to_position(double time) const
475{
477}
478
479double SoundStreamContainer::position_to_time(uint64_t position) const
480{
482}
483
485{
486 m_looping_enabled = enable;
487 if (enable && m_loop_region.start_coordinates.empty()) {
489 }
490}
491
493{
494 m_loop_region = region;
495
496 if (m_looping_enabled && !region.start_coordinates.empty()) {
497 std::vector<uint64_t> current_pos(m_read_position.size());
498 for (size_t i = 0; i < m_read_position.size(); ++i) {
499 current_pos[i] = m_read_position[i].load();
500 }
501
502 bool outside_loop = false;
503 for (size_t i = 0; i < current_pos.size() && i < region.start_coordinates.size() && i < region.end_coordinates.size(); ++i) {
504 if (current_pos[i] < region.start_coordinates[i] || current_pos[i] > region.end_coordinates[i]) {
505 outside_loop = true;
506 break;
507 }
508 }
509
510 if (outside_loop) {
512 }
513 }
514}
515
520
522{
523 auto state = get_processing_state();
524 return has_data() && (state == ProcessingState::READY || state == ProcessingState::PROCESSED);
525}
526
528{
529 std::vector<uint64_t> frames(m_num_channels);
530 if (m_looping_enabled || m_read_position.empty()) {
531
532 for (auto& frame : frames) {
533 frame = std::numeric_limits<uint64_t>::max();
534 }
535 return frames;
536 }
537
538 for (size_t i = 0; i < frames.size(); i++) {
539 uint64_t current_frame = m_read_position[i].load();
540 frames[i] = (current_frame < m_num_frames) ? (m_num_frames - current_frame) : 0;
541 }
542 return frames;
543}
544
545uint64_t SoundStreamContainer::read_sequential(std::span<double> output, uint64_t count)
546{
547 uint64_t frames_read = peek_sequential(output, count, 0);
548
549 uint64_t frames_to_advance = frames_read / m_num_channels;
550 std::vector<uint64_t> advance_amount(m_num_channels, frames_to_advance);
551
552 advance_read_position(advance_amount);
553 return frames_read;
554}
555
556uint64_t SoundStreamContainer::peek_sequential(std::span<double> output, uint64_t count, uint64_t offset) const
557{
558 auto interleaved_span = get_data_as_double();
559 if (interleaved_span.empty() || output.empty())
560 return 0;
561
562 uint64_t start_frame = m_read_position.empty() ? 0 : m_read_position[0].load();
563 start_frame += offset;
564 uint64_t elements_to_read = std::min<uint64_t>(count, static_cast<uint64_t>(output.size()));
565
566 if (!m_looping_enabled) {
567 uint64_t linear_start = start_frame * m_num_channels;
568 if (linear_start >= interleaved_span.size()) {
569 std::ranges::fill(output, 0.0);
570 return 0;
571 }
572 auto view = interleaved_span
573 | std::views::drop(linear_start)
574 | std::views::take(elements_to_read);
575
576 auto copied = std::ranges::copy(view, output.begin());
577 std::ranges::fill(output.subspan(copied.out - output.begin()), 0.0);
578 return static_cast<uint64_t>(copied.out - output.begin());
579 }
580
581 if (m_loop_region.start_coordinates.empty()) {
582 std::ranges::fill(output, 0.0);
583 return 0;
584 }
585
586 uint64_t loop_start_frame = m_loop_region.start_coordinates[0];
587 uint64_t loop_end_frame = m_loop_region.end_coordinates[0];
588 uint64_t loop_length_frames = loop_end_frame - loop_start_frame + 1;
589
590 std::ranges::for_each(
591 std::views::iota(0UZ, elements_to_read),
592 [&](uint64_t i) {
593 uint64_t element_pos = start_frame * m_num_channels + i;
594 uint64_t frame_pos = element_pos / m_num_channels;
595 uint64_t channel_offset = element_pos % m_num_channels;
596
597 uint64_t wrapped_frame = ((frame_pos - loop_start_frame) % loop_length_frames) + loop_start_frame;
598 uint64_t wrapped_element = wrapped_frame * m_num_channels + channel_offset;
599
600 output[i] = (wrapped_element < interleaved_span.size()) ? interleaved_span[wrapped_element] : 0.0;
601 });
602
603 if (elements_to_read < output.size()) {
604 std::ranges::fill(output.subspan(elements_to_read), 0.0);
605 }
606 return elements_to_read;
607}
608
610{
611 ProcessingState old_state = m_processing_state.exchange(new_state);
612
613 if (old_state != new_state) {
614 notify_state_change(new_state);
615
616 if (new_state == ProcessingState::READY) {
617 std::lock_guard<std::mutex> lock(m_reader_mutex);
618 m_consumed_dimensions.clear();
619 }
620 }
621}
622
624{
625 std::lock_guard<std::mutex> lock(m_state_mutex);
626 if (m_state_callback) {
627 m_state_callback(shared_from_this(), new_state);
628 }
629}
630
632{
633 auto state = get_processing_state();
634 return has_data() && (state == ProcessingState::READY || state == ProcessingState::PROCESSED);
635}
636
645
647{
648 auto processor = std::make_shared<ContiguousAccessProcessor>();
649 set_default_processor(processor);
650}
651
660
661void SoundStreamContainer::set_default_processor(std::shared_ptr<DataProcessor> processor)
662{
663 auto old_processor = m_default_processor;
664 m_default_processor = processor;
665
666 if (old_processor) {
667 old_processor->on_detach(shared_from_this());
668 }
669
670 if (processor) {
671 processor->on_attach(shared_from_this());
672 }
673}
674
675std::shared_ptr<DataProcessor> SoundStreamContainer::get_default_processor() const
676{
677 std::lock_guard<std::mutex> lock(m_state_mutex);
678 return m_default_processor;
679}
680
681uint32_t SoundStreamContainer::register_dimension_reader(uint32_t dimension_index)
682{
683 std::lock_guard<std::mutex> lock(m_reader_mutex);
684 m_active_readers[dimension_index]++;
685
686 uint32_t reader_id = m_dimension_to_next_reader_id[dimension_index]++;
687 m_reader_consumed_dimensions[reader_id] = std::unordered_set<uint32_t>();
688
689 return reader_id;
690}
691
693{
694 std::lock_guard<std::mutex> lock(m_reader_mutex);
695 if (auto it = m_active_readers.find(dimension_index); it != m_active_readers.end()) {
696 auto& [dim, count] = *it;
697 if (--count <= 0) {
698 m_active_readers.erase(it);
699 m_dimension_to_next_reader_id.erase(dimension_index);
700 }
701 }
702}
703
705{
706 std::lock_guard<std::mutex> lock(m_reader_mutex);
707 return !m_active_readers.empty();
708}
709
710void SoundStreamContainer::mark_dimension_consumed(uint32_t dimension_index, uint32_t reader_id)
711{
712 if (m_reader_consumed_dimensions.contains(reader_id)) {
713 m_reader_consumed_dimensions[reader_id].insert(dimension_index);
714 } else {
715 std::cerr << "WARNING: Attempted to mark dimension " << dimension_index
716 << " as consumed for unknown reader_id " << reader_id
717 << ". This may indicate the reader was not registered or has already been unregistered. "
718 << "Please ensure readers are properly registered before marking dimensions as consumed."
719 << std::endl;
720 }
721}
722
724{
725 std::lock_guard<std::mutex> lock(m_reader_mutex);
726
727 return std::ranges::all_of(m_active_readers, [this](const auto& dim_reader_pair) {
728 const auto& [dim, expected_count] = dim_reader_pair;
729
730 auto actual_count = std::ranges::count_if(m_reader_consumed_dimensions,
731 [dim](const auto& reader_dims_pair) {
732 return reader_dims_pair.second.contains(dim);
733 });
734
735 return actual_count >= expected_count;
736 });
737}
738
740{
741 std::lock_guard<std::mutex> lock(m_reader_mutex);
742
743 std::ranges::for_each(m_reader_consumed_dimensions,
744 [](auto& reader_dims_pair) {
745 reader_dims_pair.second.clear();
746 });
747}
748
750{
751 if (new_layout == m_structure.memory_layout) {
752 return;
753 }
754
756 m_structure.memory_layout = new_layout;
758 return;
759 }
760
762 m_structure.memory_layout = new_layout;
764 return;
765 }
766
767 auto current_span = convert_variant<double>(m_data[0]);
768 std::vector<double> current_data(current_span.begin(), current_span.end());
769
770 auto channels = deinterleave_channels<double>(
771 std::span<const double>(current_data.data(), current_data.size()),
773
774 std::vector<double> reorganized_data;
775 if (new_layout == MemoryLayout::ROW_MAJOR) {
776 reorganized_data = interleave_channels(channels);
777 } else {
778 reorganized_data.reserve(current_data.size());
779 for (const auto& channel : channels) {
780 reorganized_data.insert(reorganized_data.end(), channel.begin(), channel.end());
781 }
782 }
783
784 m_data[0] = DataVariant(std::move(reorganized_data));
785
787 m_double_extraction_dirty.store(true, std::memory_order_release);
788
789 m_structure.memory_layout = new_layout;
791}
792
793std::span<const double> SoundStreamContainer::get_data_as_double() const
794{
795 if (!m_double_extraction_dirty.load(std::memory_order_acquire)) {
796 return { m_cached_ext_buffer };
797 }
798
800 if (m_data.empty())
801 return {};
802
803 std::shared_lock data_lock(m_data_mutex);
804 auto span = convert_variant<double>(m_data[0]);
805 return { span.data(), span.size() };
806 }
807
808 const auto& spans = get_span_cache();
809
810 auto channels = spans
811 | std::views::transform([](const auto& span) {
812 return std::vector<double>(span.begin(), span.end());
813 })
814 | std::ranges::to<std::vector>();
815
817 m_double_extraction_dirty.store(false, std::memory_order_release);
818
819 return { m_cached_ext_buffer };
820}
821
823{
824 if (channel >= m_data.size()) {
825 throw std::out_of_range("Channel index out of range");
826 }
828}
829
831{
832 std::vector<DataAccess> result;
833 result.reserve(m_data.size());
834
835 for (auto& i : m_data) {
836 result.emplace_back(i, m_structure.dimensions, m_structure.modality);
837 }
838 return result;
839}
840
841const std::vector<std::span<double>>& SoundStreamContainer::get_span_cache() const
842{
843 if (!m_span_cache_dirty.load(std::memory_order_acquire) && m_span_cache.has_value()) {
844 return *m_span_cache;
845 }
846
847 std::unique_lock data_lock(m_data_mutex);
848
849 if (!m_span_cache_dirty.load(std::memory_order_acquire) && m_span_cache.has_value()) {
850 return *m_span_cache;
851 }
852
853 auto spans = m_data
854 | std::views::transform([](auto& variant) {
855 return convert_variant<double>(const_cast<DataVariant&>(variant));
856 })
857 | std::ranges::to<std::vector>();
858
859 m_span_cache = std::move(spans);
860 m_span_cache_dirty.store(false, std::memory_order_release);
861
862 return *m_span_cache;
863}
864
866{
867 m_span_cache_dirty.store(true, std::memory_order_release);
868}
869
870}
Type-erased accessor for NDData with semantic view construction.
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.
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.
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.
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 set_default_processor(std::shared_ptr< DataProcessor > processor) override
Set the default data processor for this container.
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.
const RegionGroup & get_region_group(const std::string &name) const override
Get a region group by name.
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.
std::span< const double > get_frame(uint64_t frame_index) const override
Get a single frame of data efficiently.
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.
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.
void get_frames(std::span< double > output, uint64_t start_frame, uint64_t num_frames) const override
Get multiple frames efficiently.
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.
void lock() override
Acquire a lock for thread-safe access.
void set_value_at(const std::vector< uint64_t > &coordinates, double value) override
Set a single value at the specified coordinates.
std::vector< DataVariant > get_region_group_data(const RegionGroup &group) const override
Get data for multiple regions efficiently.
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
double get_value_at(const std::vector< uint64_t > &coordinates) const override
Get a single value at the specified coordinates.
std::shared_ptr< DataProcessor > get_default_processor() const override
Get the current default data processor.
uint64_t get_frame_size() const override
Get the number of elements that constitute one "frame".
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:6
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:73
DataModality
Data modality types for cross-modal analysis.
Definition NDData.hpp:78
@ 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:36
@ 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:46
@ 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:105
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:135
std::vector< uint64_t > end_coordinates
Ending frame index (inclusive)
Definition Region.hpp:72
std::vector< uint64_t > start_coordinates
Starting frame index (inclusive)
Definition Region.hpp:69
Represents a point or span in N-dimensional space.
Definition Region.hpp:67