62 double mean_energy {};
68 std::array<int, 5> level_counts {};
83 uint32_t window_size {};
108template <ComputeData InputType = std::vector<Kakshya::DataVariant>, ComputeData OutputType = Eigen::VectorXd>
121 : m_window_size(window_size)
122 , m_hop_size(hop_size)
124 validate_window_parameters();
134 auto result = this->analyze_data(data);
135 return safe_any_cast_or_throw<EnergyAnalysis>(result);
144 return safe_any_cast_or_throw<EnergyAnalysis>(this->get_current_analysis());
153 return AnalysisType::FEATURE;
162 return Reflect::get_enum_names_lowercase<EnergyMethod>();
172 return std::find_if(get_available_methods().begin(), get_available_methods().end(),
173 [&method](
const std::string& m) {
174 return std::equal(method.begin(), method.end(), m.begin(), m.end(),
175 [](
char a,
char b) { return std::tolower(a) == std::tolower(b); });
177 != get_available_methods().end();
197 m_window_size = window_size;
198 m_hop_size = hop_size;
199 validate_window_parameters();
211 if (silent >= quiet || quiet >= moderate || moderate >= loud) {
212 error<std::invalid_argument>(Journal::Component::Yantra, Journal::Context::ComputeMatrix, std::source_location::current(),
"Energy thresholds must be in ascending order");
214 m_silent_threshold = silent;
215 m_quiet_threshold = quiet;
216 m_moderate_threshold = moderate;
217 m_loud_threshold = loud;
233 if (energy <= m_silent_threshold)
234 return EnergyLevel::SILENT;
235 if (energy <= m_quiet_threshold)
236 return EnergyLevel::QUIET;
237 if (energy <= m_moderate_threshold)
238 return EnergyLevel::MODERATE;
239 if (energy <= m_loud_threshold)
240 return EnergyLevel::LOUD;
241 return EnergyLevel::PEAK;
253 return channel.
level_counts[
static_cast<size_t>(level)];
263 return Reflect::enum_to_lowercase_string(method);
273 if (str ==
"default")
274 return EnergyMethod::RMS;
275 return Reflect::string_to_enum_or_throw_case_insensitive<EnergyMethod>(str,
"EnergyMethod");
285 return Reflect::enum_to_lowercase_string(level);
295 return "EnergyAnalyzer";
306 auto [data_span, structure_info] = OperationHelper::extract_structured_double(
309 std::vector<std::span<const double>> channel_spans;
310 for (
auto& span : data_span)
311 channel_spans.emplace_back(span.data(), span.size());
313 for (
const auto& channel_span : channel_spans) {
314 if (channel_span.size() < m_window_size) {
315 error<std::runtime_error>(Journal::Component::Yantra, Journal::Context::ComputeMatrix, std::source_location::current(),
"One or more channels in input data are smaller than window size ({} samples)", m_window_size);
319 std::vector<std::vector<double>> energy_values;
320 energy_values.reserve(channel_spans.size());
321 for (
const auto& channel_span : channel_spans) {
322 energy_values.push_back(compute_energy_values(channel_span, m_method));
326 energy_values, channel_spans, structure_info);
328 this->store_current_analysis(analysis_result);
330 return create_pipeline_output(input, analysis_result, structure_info);
331 }
catch (
const std::exception& e) {
332 MF_ERROR(Journal::Component::Yantra, Journal::Context::ComputeMatrix,
"Energy analysis failed: {}", e.what());
335 error_result.
metadata[
"error"] = std::string(
"Analysis failed: ") + e.what();
345 if (name ==
"method") {
347 auto method_str = safe_any_cast_or_throw<std::string>(value);
348 m_method = string_to_method(method_str);
350 }
catch (
const std::runtime_error&) {
351 error_rethrow(Journal::Component::Yantra, Journal::Context::ComputeMatrix, std::source_location::current(),
"Invalid method parameter - expected string or EnergyMethod enum");
353 if (
auto* method_enum = std::any_cast<EnergyMethod>(&value)) {
354 m_method = *method_enum;
357 }
else if (name ==
"window_size") {
358 if (
auto* size = std::any_cast<uint32_t>(&value)) {
359 m_window_size = *size;
360 validate_window_parameters();
363 }
else if (name ==
"hop_size") {
364 if (
auto* size = std::any_cast<uint32_t>(&value)) {
366 validate_window_parameters();
369 }
else if (name ==
"classification_enabled") {
370 if (
auto* enabled = std::any_cast<bool>(&value)) {
371 m_classification_enabled = *enabled;
376 base_type::set_analysis_parameter(name, std::move(value));
384 if (name ==
"method")
385 return std::any(method_to_string(m_method));
386 if (name ==
"window_size")
387 return std::any(m_window_size);
388 if (name ==
"hop_size")
389 return std::any(m_hop_size);
390 if (name ==
"classification_enabled")
391 return std::any(m_classification_enabled);
393 return base_type::get_analysis_parameter(name);
397 uint32_t m_window_size { 512 };
398 uint32_t m_hop_size { 256 };
400 bool m_classification_enabled {
false };
402 double m_silent_threshold { 0.01 };
403 double m_quiet_threshold = { 0.1 };
404 double m_moderate_threshold { 0.5 };
405 double m_loud_threshold { 0.8 };
409 if (m_window_size == 0) {
410 error<std::invalid_argument>(Journal::Component::Yantra, Journal::Context::ComputeMatrix, std::source_location::current(),
"Window size must be greater than 0");
412 if (m_hop_size == 0) {
413 error<std::invalid_argument>(Journal::Component::Yantra, Journal::Context::ComputeMatrix, std::source_location::current(),
"Hop size must be greater than 0");
415 if (m_hop_size > m_window_size) {
416 error<std::invalid_argument>(Journal::Component::Yantra, Journal::Context::ComputeMatrix, std::source_location::current(),
"Hop size should not exceed window size");
424 const std::vector<std::vector<double>>& energy_values,
425 std::vector<std::span<const double>> original_data,
433 if (energy_values.empty()) {
437 result.
channels.resize(energy_values.size());
439 for (
size_t ch = 0; ch < energy_values.size(); ch++) {
441 auto& channel_result = result.
channels[ch];
442 const auto& ch_energy = energy_values[ch];
444 channel_result.energy_values = ch_energy;
446 if (!ch_energy.empty()) {
447 auto [min_it, max_it] = std::ranges::minmax_element(ch_energy);
448 channel_result.min_energy = *min_it;
449 channel_result.max_energy = *max_it;
451 const double sum = std::accumulate(ch_energy.begin(), ch_energy.end(), 0.0);
452 channel_result.mean_energy = sum /
static_cast<double>(ch_energy.size());
454 const double mean = channel_result.mean_energy;
455 const double var_sum = std::transform_reduce(
456 ch_energy.begin(), ch_energy.end(), 0.0, std::plus {},
457 [
mean](
double val) { return (val - mean) * (val - mean); });
458 channel_result.variance = var_sum /
static_cast<double>(ch_energy.size());
461 const size_t data_size = (ch < original_data.size()) ? original_data[ch].size() : 0;
462 channel_result.window_positions.reserve(ch_energy.size());
464 for (
size_t i = 0; i < ch_energy.size(); ++i) {
465 const size_t start = i * m_hop_size;
466 const size_t end = std::min(start + m_window_size, data_size);
467 channel_result.window_positions.emplace_back(start, end);
470 if (ch < original_data.size()) {
472 case EnergyMethod::ZERO_CROSSING:
473 channel_result.event_positions = Kinesis::Discrete::zero_crossing_positions(
474 original_data[ch], 0.0);
477 case EnergyMethod::PEAK: {
478 double peak_threshold = m_classification_enabled ? m_quiet_threshold : 0.01;
479 channel_result.event_positions = Kinesis::Discrete::peak_positions(
480 original_data[ch], peak_threshold, m_hop_size / 4);
484 case EnergyMethod::RMS:
485 case EnergyMethod::POWER:
486 case EnergyMethod::SPECTRAL:
487 case EnergyMethod::HARMONIC:
488 case EnergyMethod::DYNAMIC_RANGE:
495 if (m_classification_enabled) {
496 channel_result.classifications.reserve(ch_energy.size());
497 channel_result.level_counts.fill(0);
499 for (
double energy : ch_energy) {
501 channel_result.classifications.push_back(level);
502 channel_result.level_counts[
static_cast<size_t>(level)]++;
515 std::vector<std::vector<double>> channel_energies;
516 channel_energies.reserve(analysis_result.
channels.size());
518 for (
const auto& ch : analysis_result.
channels) {
519 channel_energies.push_back(ch.energy_values);
522 output_type output = this->convert_result(channel_energies, info);
526 output.
metadata[
"source_analyzer"] =
"EnergyAnalyzer";
532 if (!analysis_result.
channels.empty()) {
533 std::vector<double> channel_means, channel_maxs, channel_mins, channel_variances;
534 std::vector<size_t> channel_window_counts;
536 for (
const auto& ch : analysis_result.
channels) {
537 channel_means.push_back(ch.mean_energy);
538 channel_maxs.push_back(ch.max_energy);
539 channel_mins.push_back(ch.min_energy);
540 channel_variances.push_back(ch.variance);
541 channel_window_counts.push_back(ch.energy_values.size());
544 output.
metadata[
"mean_energy_per_channel"] = channel_means;
545 output.
metadata[
"max_energy_per_channel"] = channel_maxs;
546 output.
metadata[
"min_energy_per_channel"] = channel_mins;
547 output.
metadata[
"variance_per_channel"] = channel_variances;
548 output.
metadata[
"window_count_per_channel"] = channel_window_counts;
560 const size_t num_windows = calculate_num_windows(data.size());
563 case EnergyMethod::PEAK:
564 return D::peak(data, num_windows, m_hop_size, m_window_size);
565 case EnergyMethod::ZERO_CROSSING:
566 return D::zero_crossing_rate(data, num_windows, m_hop_size, m_window_size);
567 case EnergyMethod::POWER:
568 return D::power(data, num_windows, m_hop_size, m_window_size);
569 case EnergyMethod::DYNAMIC_RANGE:
570 return D::dynamic_range(data, num_windows, m_hop_size, m_window_size);
571 case EnergyMethod::SPECTRAL:
572 return D::spectral_energy(data, num_windows, m_hop_size, m_window_size);
573 case EnergyMethod::HARMONIC:
574 return D::low_frequency_energy(data, num_windows, m_hop_size, m_window_size);
575 case EnergyMethod::RMS:
577 return D::rms(data, num_windows, m_hop_size, m_window_size);
586 if (data_size < m_window_size)
588 return (data_size - m_window_size) / m_hop_size + 1;
602template <ComputeData InputType = std::vector<Kakshya::DataVariant>>
606template <ComputeData InputType = std::vector<Kakshya::DataVariant>>
Discrete sequence analysis primitives for MayaFlux::Kinesis.
#define MF_ERROR(comp, ctx,...)
Modern, digital-first universal analyzer framework for Maya Flux.
EnergyMethod get_energy_method() const
Get current energy computation method.
size_t calculate_num_windows(size_t data_size) const
Calculate number of windows for given data size.
bool supports_method(const std::string &method) const override
Check if a specific method is supported.
output_type create_pipeline_output(const input_type &input, const EnergyAnalysis &analysis_result, DataStructureInfo &info)
Create pipeline output from input and energy values.
static EnergyMethod string_to_method(const std::string &str)
Convert string to energy method enum.
void enable_classification(bool enabled)
Enable or disable energy level classification.
AnalysisType get_analysis_type() const override
Get analysis type category.
EnergyAnalyzer(uint32_t window_size=256, uint32_t hop_size=128)
Construct EnergyAnalyzer with configurable window parameters.
EnergyAnalysis create_analysis_result(const std::vector< std::vector< double > > &energy_values, std::vector< std::span< const double > > original_data, const DataStructureInfo &) const
Create comprehensive analysis result from energy computation.
void validate_window_parameters() const
void set_energy_method(EnergyMethod method)
Set energy computation method.
std::any get_analysis_parameter(const std::string &name) const override
Get analysis-specific parameter.
int get_level_count(const ChannelEnergy &channel, EnergyLevel level)
Get count of windows in a specific energy level for a channel.
static std::string method_to_string(EnergyMethod method)
Convert energy method enum to string.
output_type analyze_implementation(const input_type &input) override
Core analysis implementation - creates analysis result AND pipeline output.
void set_energy_thresholds(double silent, double quiet, double moderate, double loud)
Set energy level classification thresholds.
std::vector< double > compute_energy_values(std::span< const double > data, EnergyMethod method) const
Compute energy values using span (zero-copy processing)
std::vector< std::string > get_available_methods() const override
Get available analysis methods.
void set_window_parameters(uint32_t window_size, uint32_t hop_size)
Set window parameters for analysis.
static std::string energy_level_to_string(EnergyLevel level)
Convert energy level enum to string.
std::string get_analyzer_name() const override
Get analyzer name.
EnergyAnalysis analyze_energy(const InputType &data)
Type-safe energy analysis method.
void set_analysis_parameter(const std::string &name, std::any value) override
Handle analysis-specific parameters.
EnergyLevel classify_energy_level(double energy) const
Classify energy value into qualitative level.
EnergyAnalysis get_energy_analysis() const
Get last energy analysis result (type-safe)
High-performance energy analyzer with zero-copy processing.
Template-flexible analyzer base with instance-defined I/O types.
AnalysisType
Categories of analysis operations for discovery and organization.
EnergyMethod
Supported energy computation methods.
@ SPECTRAL
Spectral energy (FFT-based)
@ DYNAMIC_RANGE
Dynamic range (dB)
@ RMS
Root Mean Square energy.
@ ZERO_CROSSING
Zero-crossing rate.
@ HARMONIC
Harmonic energy (low-frequency content)
@ POWER
Power (sum of squares)
EnergyLevel
Qualitative classification of energy values.
double mean(const std::vector< double > &data)
Calculate mean of single-channel data.
std::vector< size_t > event_positions
std::vector< EnergyLevel > classifications
std::vector< std::pair< size_t, size_t > > window_positions
std::array< int, 5 > level_counts
std::vector< double > energy_values
Metadata about data structure for reconstruction.
std::unordered_map< std::string, std::any > metadata
Associated metadata.
Input/Output container for computation pipeline data flow with structure preservation.
std::vector< ChannelEnergy > channels
Analysis result structure for energy analysis.