MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
SoundContainerBuffer.cpp
Go to the documentation of this file.
2
4
6
7namespace MayaFlux::Buffers {
8
9SoundStreamReader::SoundStreamReader(const std::shared_ptr<Kakshya::StreamContainer>& container)
10 : m_container(container)
11{
12 if (container) {
13 auto structure = container->get_structure();
14 m_num_channels = structure.get_channel_count();
15
16 container->register_state_change_callback(
17 [this](auto c, auto s) {
18 this->on_container_state_change(c, s);
19 });
20 }
21}
22
23void SoundStreamReader::processing_function(const std::shared_ptr<Buffer>& buffer)
24{
25 if (!m_container || !buffer) {
26 return;
27 }
28
29 if (m_container->is_at_end()) {
30 buffer->mark_for_removal();
31 return;
32 }
33
34 try {
35 auto state = m_container->get_processing_state();
36
38 if (m_update_flags) {
39 buffer->mark_for_removal();
40 }
41 return;
42 }
43
46 if (m_container->try_acquire_processing_token(m_source_channel)) {
47 m_container->process_default();
48 }
49 }
50 }
51
52 auto audio_buffer = std::dynamic_pointer_cast<AudioBuffer>(buffer);
53 auto& buffer_data = audio_buffer->get_data();
54 uint32_t buffer_size = audio_buffer->get_num_samples();
55
56 auto read_positions = m_container->get_read_position();
57 uint64_t current_pos = (m_source_channel < read_positions.size())
58 ? read_positions[m_source_channel]
59 : 0;
60
61 if (buffer_data.size() != buffer_size) {
62 buffer_data.resize(buffer_size);
63 }
64
65 extract_channel_data(buffer_data);
66
67 if (m_auto_advance) {
68 m_container->update_read_position_for_channel(m_source_channel, current_pos + buffer_size);
69 }
70
71 if (m_update_flags) {
72 buffer->mark_for_processing(true);
73 }
74
75 m_container->mark_dimension_consumed(m_source_channel, m_reader_id);
76
77 if (m_container->all_dimensions_consumed()) {
78 m_container->update_processing_state(Kakshya::ProcessingState::READY);
79 std::dynamic_pointer_cast<Kakshya::SoundFileContainer>(m_container)->clear_all_consumption();
80 m_container->reset_processing_token();
81 }
82
83 } catch (const std::exception& e) {
85 "SoundStreamReader: Error during processing: {}", e.what());
86 }
87}
88
89void SoundStreamReader::extract_channel_data(std::span<double> output)
90{
91 auto sc = std::dynamic_pointer_cast<Kakshya::SoundStreamContainer>(m_container);
92 if (!sc) {
93 std::ranges::fill(output, 0.0);
94 return;
95 }
96
97 const auto& pd = sc->get_processed_data();
98 const auto structure = sc->get_structure();
99
101 pd, structure.organization, structure.get_channel_count(),
102 m_source_channel, output);
103}
104
105void SoundStreamReader::on_attach(const std::shared_ptr<Buffer>& buffer)
106{
107 if (!m_container || !buffer) {
108 return;
109 }
110
111 m_reader_id = m_container->register_dimension_reader(m_source_channel);
112
113 if (!m_container->is_ready_for_processing()) {
115 std::source_location::current(),
116 "SoundStreamReader: Container not ready for processing");
117 }
118
119 try {
120 auto& buffer_data = std::dynamic_pointer_cast<AudioBuffer>(buffer)->get_data();
121 uint32_t num_samples = std::dynamic_pointer_cast<AudioBuffer>(buffer)->get_num_samples();
122
123 extract_channel_data(buffer_data);
124
125 if (m_update_flags) {
126 buffer->mark_for_processing(true);
127 }
128
129 } catch (const std::exception& e) {
131 "SoundStreamReader: Error pre-filling buffer: {}", e.what());
132 }
133}
134
135void SoundStreamReader::on_detach(const std::shared_ptr<Buffer>& /*buffer*/)
136{
137 if (m_container) {
138 m_container->unregister_state_change_callback();
139 m_container->unregister_dimension_reader(m_source_channel);
140 }
141}
142
143void SoundStreamReader::set_source_channel(uint32_t channel_index)
144{
145 if (channel_index >= m_num_channels) {
147 std::source_location::current(),
148 "SoundStreamReader: Channel index {} exceeds container channel count {}",
149 channel_index, m_num_channels);
150 }
151 m_source_channel = channel_index;
152}
153
154void SoundStreamReader::set_container(const std::shared_ptr<Kakshya::StreamContainer>& container)
155{
156 if (m_container) {
157 m_container->unregister_state_change_callback();
158 }
159
160 m_container = container;
161
162 if (container) {
163 auto structure = container->get_structure();
164 m_num_channels = structure.get_channel_count();
165
166 container->register_state_change_callback(
167 [this](std::shared_ptr<Kakshya::SignalSourceContainer> c, Kakshya::ProcessingState s) {
168 this->on_container_state_change(c, s);
169 });
170 }
171}
172
174 const std::shared_ptr<Kakshya::SignalSourceContainer>& /*container*/,
176{
177 switch (state) {
179 break;
180
183 "SoundStreamReader: Container entered ERROR state");
184 break;
185
186 default:
187 break;
188 }
189}
190
191SoundContainerBuffer::SoundContainerBuffer(uint32_t channel_id, uint32_t num_samples,
192 const std::shared_ptr<Kakshya::StreamContainer>& container,
193 uint32_t source_channel)
194 : AudioBuffer(channel_id, num_samples)
195 , m_container(container)
196 , m_source_channel(source_channel)
197{
198 if (!m_container) {
199 error<std::invalid_argument>(Journal::Component::Buffers, Journal::Context::Init,
200 std::source_location::current(),
201 "SoundContainerBuffer: container must not be null");
202 }
203
204 m_pending_adapter = std::make_shared<SoundStreamReader>(m_container);
205 std::dynamic_pointer_cast<SoundStreamReader>(m_pending_adapter)->set_source_channel(m_source_channel);
206
208}
209
218
219void SoundContainerBuffer::set_container(const std::shared_ptr<Kakshya::StreamContainer>& container)
220{
221 m_container = container;
222
223 if (auto adapter = std::dynamic_pointer_cast<SoundStreamReader>(m_default_processor)) {
224 adapter->set_container(container);
225 }
226
228}
229
231{
232 // Check if we can use zero-copy mode
233 // This would be possible if:
234 // 1. Container data is contiguous doubles
235 // 2. Channel is deinterleaved (column-major for audio)
236 // 3. Buffer size matches container frame size
237
238 if (!m_container) {
239 m_zero_copy_mode = false;
240 return;
241 }
242
243 auto dimensions = m_container->get_dimensions();
244 auto layout = m_container->get_memory_layout();
245
246 m_zero_copy_mode = false;
247
248 // TODO: Implement zero-copy when container provides direct memory access
249}
250
251std::shared_ptr<BufferProcessor> SoundContainerBuffer::create_default_processor()
252{
253 if (m_pending_adapter) {
254 return m_pending_adapter;
255 }
256
257 auto adapter = std::make_shared<SoundStreamReader>(m_container);
258 adapter->set_source_channel(m_source_channel);
259 return adapter;
260}
261
262} // namespace MayaFlux::Buffers
#define MF_ERROR(comp, ctx,...)
void enforce_default_processing(bool should_process) override
Controls whether the audio buffer should use default processing.
std::shared_ptr< BufferProcessor > m_default_processor
Default audio transformation processor for this buffer.
void set_default_processor(const std::shared_ptr< BufferProcessor > &processor) override
Sets the default audio transformation processor for this buffer.
Concrete audio implementation of the Buffer interface for double-precision audio data.
void initialize()
Initialize the buffer after construction.
void setup_zero_copy_if_possible()
Attempt to enable zero-copy operation if container layout allows.
std::shared_ptr< Kakshya::StreamContainer > m_container
void set_container(const std::shared_ptr< Kakshya::StreamContainer > &container)
Update the container reference.
SoundContainerBuffer(uint32_t channel_id, uint32_t num_samples, const std::shared_ptr< Kakshya::StreamContainer > &container, uint32_t source_channel=0)
Construct a SoundContainerBuffer for a specific channel and container.
std::shared_ptr< BufferProcessor > m_pending_adapter
std::shared_ptr< BufferProcessor > create_default_processor() override
Create the default processor (SoundStreamReader) for this buffer.
void set_container(const std::shared_ptr< Kakshya::StreamContainer > &container)
Set the container to adapt.
void on_detach(const std::shared_ptr< Buffer > &buffer) override
Detach the adapter from its AudioBuffer.
std::shared_ptr< Kakshya::StreamContainer > m_container
void extract_channel_data(std::span< double > output)
Extract channel data from the container into the output buffer.
void on_attach(const std::shared_ptr< Buffer > &buffer) override
Attach the adapter to an AudioBuffer.
void processing_function(const std::shared_ptr< Buffer > &buffer) override
Extracts and processes data from the container into the target AudioBuffer.
void on_container_state_change(const std::shared_ptr< Kakshya::SignalSourceContainer > &container, Kakshya::ProcessingState state)
Respond to container state changes (e.g., READY, PROCESSED, NEEDS_REMOVAL).
void set_source_channel(uint32_t channel_index)
Set which channel dimension to extract from the container.
SoundStreamReader(const std::shared_ptr< Kakshya::StreamContainer > &container)
@ BufferProcessing
Buffer processing (Buffers::BufferManager, processing chains)
@ Init
Engine/subsystem initialization.
@ Buffers
Buffers, Managers, processors and processing chains.
ProcessingState
Represents the current processing lifecycle state of a container.
@ READY
Container has data loaded and is ready for processing.
@ NEEDS_REMOVAL
Container is marked for removal from the system.
@ ERROR
Container is in an error state and cannot proceed.
@ PROCESSED
Container has completed processing and results are available.
void extract_processed_data(const std::vector< DataVariant > &pd, OrganizationStrategy organization, uint64_t num_channels, uint32_t ch, std::span< double > output)
Extract one channel's samples from a processed dynamic data block.