Grammar rule executor for additive grain reconstruction.
160 {
161 auto datum = safe_any_cast_or_throw<Datum<Kakshya::RegionGroup>>(input);
162 const auto& regions = datum.data.regions;
163
164 if (regions.empty()) {
165 error<std::runtime_error>(Journal::Component::Yantra, Journal::Context::ComputeMatrix,
166 std::source_location::current(), "reconstruct_grains_additive: RegionGroup contains no grains");
167 }
168
169 if (!datum.container || !*datum.container) {
170 error<std::runtime_error>(Journal::Component::Yantra, Journal::Context::ComputeMatrix,
171 std::source_location::current(), "reconstruct_grains_additive: no source container in datum");
172 }
173
174 auto source = *datum.container;
175 const auto num_ch = static_cast<uint32_t>(source->get_structure().get_channel_count());
176 const auto org = source->get_structure().organization;
177 const uint32_t sample_rate = [&]() -> uint32_t {
178 if (auto sc = std::dynamic_pointer_cast<Kakshya::SoundStreamContainer>(source))
179 return sc->get_sample_rate();
180 return 48000U;
181 }();
182
183 const auto grain_sz = safe_any_cast_or_default<uint32_t>(
184 datum.data.get_attribute<uint32_t>("grain_size").has_value()
185 ? std::any(datum.data.get_attribute<uint32_t>("grain_size").value())
187 1024U);
188 const auto hop_sz = safe_any_cast_or_default<uint32_t>(
189 datum.data.get_attribute<uint32_t>("hop_size").has_value()
190 ? std::any(datum.data.get_attribute<uint32_t>("hop_size").value())
192 grain_sz);
193
194 const auto taper = safe_any_cast_or_default<GrainTaper>(
195 ctx.execution_metadata.contains("grain_taper")
196 ? ctx.execution_metadata.at("grain_taper")
198 GrainTaper {});
199
200 const size_t out_len = (regions.size() - 1) * hop_sz + grain_sz;
201 std::vector<std::vector<double>> channel_data(num_ch, std::vector<double>(out_len, 0.0));
202
203 Kakshya::iterate_region_channels(regions, source, num_ch, taper,
204 [&channel_data, hop_sz](size_t gi, uint32_t ch, std::span<double> samples) {
205 const size_t offset = gi * hop_sz;
206 auto& dest = channel_data[ch];
207 for (size_t s = 0; s < samples.size() && offset + s < dest.size(); ++s)
208 dest[offset + s] += samples[s];
209 });
210
211 return Datum<std::shared_ptr<Kakshya::SignalSourceContainer>> {
212 std::dynamic_pointer_cast<Kakshya::SignalSourceContainer>(
213 Kakshya::make_sound_file_container(std::move(channel_data), num_ch, sample_rate, org)),
214 {}
215 };
216};