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);
140 return this->analyze_energy(
input_type { data });
149 return safe_any_cast_or_throw<EnergyAnalysis>(this->get_current_analysis());
158 return AnalysisType::FEATURE;
167 return Reflect::get_enum_names_lowercase<EnergyMethod>();
177 return std::find_if(get_available_methods().begin(), get_available_methods().end(),
178 [&method](
const std::string& m) {
179 return std::equal(method.begin(), method.end(), m.begin(), m.end(),
180 [](
char a,
char b) { return std::tolower(a) == std::tolower(b); });
182 != get_available_methods().end();
202 m_window_size = window_size;
203 m_hop_size = hop_size;
204 validate_window_parameters();
216 if (silent >= quiet || quiet >= moderate || moderate >= loud) {
217 error<std::invalid_argument>(Journal::Component::Yantra, Journal::Context::ComputeMatrix, std::source_location::current(),
"Energy thresholds must be in ascending order");
219 m_silent_threshold = silent;
220 m_quiet_threshold = quiet;
221 m_moderate_threshold = moderate;
222 m_loud_threshold = loud;
238 if (energy <= m_silent_threshold)
239 return EnergyLevel::SILENT;
240 if (energy <= m_quiet_threshold)
241 return EnergyLevel::QUIET;
242 if (energy <= m_moderate_threshold)
243 return EnergyLevel::MODERATE;
244 if (energy <= m_loud_threshold)
245 return EnergyLevel::LOUD;
246 return EnergyLevel::PEAK;
258 return channel.level_counts[
static_cast<size_t>(level)];
268 return Reflect::enum_to_lowercase_string(method);
278 if (str ==
"default")
279 return EnergyMethod::RMS;
280 return Reflect::string_to_enum_or_throw_case_insensitive<EnergyMethod>(str,
"EnergyMethod");
290 return Reflect::enum_to_lowercase_string(level);
300 return "EnergyAnalyzer";
311 auto [data_span, structure_info] = OperationHelper::extract_structured_double(
314 std::vector<std::span<const double>> channel_spans;
315 for (
auto& span : data_span)
316 channel_spans.emplace_back(span.data(), span.size());
318 for (
const auto& channel_span : channel_spans) {
319 if (channel_span.size() < m_window_size) {
320 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);
324 std::vector<std::vector<double>> energy_values;
325 energy_values.reserve(channel_spans.size());
326 for (
const auto& channel_span : channel_spans) {
327 energy_values.push_back(compute_energy_values(channel_span, m_method));
331 energy_values, channel_spans, structure_info);
333 this->store_current_analysis(analysis_result);
335 return create_pipeline_output(input, analysis_result, structure_info);
336 }
catch (
const std::exception& e) {
337 MF_ERROR(Journal::Component::Yantra, Journal::Context::ComputeMatrix,
"Energy analysis failed: {}", e.what());
340 error_result.
metadata[
"error"] = std::string(
"Analysis failed: ") + e.what();
350 if (name ==
"method") {
351 if (
auto str_result = safe_any_cast<std::string>(value)) {
352 m_method = string_to_method(*str_result.value);
356 if (
auto enum_result = safe_any_cast<EnergyMethod>(value)) {
357 m_method = *enum_result.value;
361 error<std::invalid_argument>(Journal::Component::Yantra, Journal::Context::ComputeMatrix,
362 std::source_location::current(),
363 "Invalid method parameter - expected string or EnergyMethod enum");
364 }
else if (name ==
"window_size") {
365 if (
auto result = safe_any_cast<uint32_t>(value)) {
366 m_window_size = *result.value;
367 validate_window_parameters();
370 }
else if (name ==
"hop_size") {
371 if (
auto result = safe_any_cast<uint32_t>(value)) {
372 m_hop_size = *result.value;
373 validate_window_parameters();
376 }
else if (name ==
"classification_enabled") {
377 if (
auto result = safe_any_cast<bool>(value)) {
378 m_classification_enabled = *result.value;
383 base_type::set_analysis_parameter(name, std::move(value));
391 if (name ==
"method")
392 return std::any(method_to_string(m_method));
393 if (name ==
"window_size")
394 return std::any(m_window_size);
395 if (name ==
"hop_size")
396 return std::any(m_hop_size);
397 if (name ==
"classification_enabled")
398 return std::any(m_classification_enabled);
400 return base_type::get_analysis_parameter(name);
404 uint32_t m_window_size { 512 };
405 uint32_t m_hop_size { 256 };
407 bool m_classification_enabled {
false };
409 double m_silent_threshold { 0.01 };
410 double m_quiet_threshold = { 0.1 };
411 double m_moderate_threshold { 0.5 };
412 double m_loud_threshold { 0.8 };
416 if (m_window_size == 0) {
417 error<std::invalid_argument>(Journal::Component::Yantra, Journal::Context::ComputeMatrix, std::source_location::current(),
"Window size must be greater than 0");
419 if (m_hop_size == 0) {
420 error<std::invalid_argument>(Journal::Component::Yantra, Journal::Context::ComputeMatrix, std::source_location::current(),
"Hop size must be greater than 0");
422 if (m_hop_size > m_window_size) {
423 error<std::invalid_argument>(Journal::Component::Yantra, Journal::Context::ComputeMatrix, std::source_location::current(),
"Hop size should not exceed window size");
431 const std::vector<std::vector<double>>& energy_values,
432 std::vector<std::span<const double>> original_data,
440 if (energy_values.empty()) {
444 result.
channels.resize(energy_values.size());
446 for (
size_t ch = 0; ch < energy_values.size(); ch++) {
448 auto& channel_result = result.
channels[ch];
449 const auto& ch_energy = energy_values[ch];
451 channel_result.energy_values = ch_energy;
453 if (!ch_energy.empty()) {
454 auto [min_it, max_it] = std::ranges::minmax_element(ch_energy);
455 channel_result.min_energy = *min_it;
456 channel_result.max_energy = *max_it;
458 const double sum = std::accumulate(ch_energy.begin(), ch_energy.end(), 0.0);
459 channel_result.mean_energy = sum /
static_cast<double>(ch_energy.size());
461 const double mean = channel_result.mean_energy;
462 const double var_sum = std::transform_reduce(
463 ch_energy.begin(), ch_energy.end(), 0.0, std::plus {},
464 [
mean](
double val) { return (val - mean) * (val - mean); });
465 channel_result.variance = var_sum /
static_cast<double>(ch_energy.size());
468 const size_t data_size = (ch < original_data.size()) ? original_data[ch].
size() : 0;
469 channel_result.window_positions.reserve(ch_energy.size());
471 for (
size_t i = 0; i < ch_energy.size(); ++i) {
472 const size_t start = i * m_hop_size;
473 const size_t end = std::min(start + m_window_size, data_size);
474 channel_result.window_positions.emplace_back(start, end);
477 if (ch < original_data.size()) {
479 case EnergyMethod::ZERO_CROSSING:
480 channel_result.event_positions = Kinesis::Discrete::zero_crossing_positions(
481 original_data[ch], 0.0);
484 case EnergyMethod::PEAK: {
485 double peak_threshold = m_classification_enabled ? m_quiet_threshold : 0.01;
486 channel_result.event_positions = Kinesis::Discrete::peak_positions(
487 original_data[ch], peak_threshold, m_hop_size / 4);
491 case EnergyMethod::RMS:
492 case EnergyMethod::POWER:
493 case EnergyMethod::SPECTRAL:
494 case EnergyMethod::HARMONIC:
495 case EnergyMethod::DYNAMIC_RANGE:
502 if (m_classification_enabled) {
503 channel_result.classifications.reserve(ch_energy.size());
504 channel_result.level_counts.fill(0);
506 for (
double energy : ch_energy) {
508 channel_result.classifications.push_back(level);
509 channel_result.level_counts[
static_cast<size_t>(level)]++;
522 std::vector<std::vector<double>> channel_energies;
523 channel_energies.reserve(analysis_result.
channels.size());
525 for (
const auto& ch : analysis_result.
channels) {
526 channel_energies.push_back(ch.energy_values);
529 output_type output = this->convert_result(channel_energies, info);
533 output.
metadata[
"source_analyzer"] =
"EnergyAnalyzer";
539 if (!analysis_result.
channels.empty()) {
540 std::vector<double> channel_means, channel_maxs, channel_mins, channel_variances;
541 std::vector<size_t> channel_window_counts;
543 for (
const auto& ch : analysis_result.
channels) {
544 channel_means.push_back(ch.mean_energy);
545 channel_maxs.push_back(ch.max_energy);
546 channel_mins.push_back(ch.min_energy);
547 channel_variances.push_back(ch.variance);
548 channel_window_counts.push_back(ch.energy_values.size());
551 output.
metadata[
"mean_energy_per_channel"] = channel_means;
552 output.
metadata[
"max_energy_per_channel"] = channel_maxs;
553 output.
metadata[
"min_energy_per_channel"] = channel_mins;
554 output.
metadata[
"variance_per_channel"] = channel_variances;
555 output.
metadata[
"window_count_per_channel"] = channel_window_counts;
567 const size_t num_windows = calculate_num_windows(data.size());
570 case EnergyMethod::PEAK:
571 return D::peak(data, num_windows, m_hop_size, m_window_size);
572 case EnergyMethod::ZERO_CROSSING:
573 return D::zero_crossing_rate(data, num_windows, m_hop_size, m_window_size);
574 case EnergyMethod::POWER:
575 return D::power(data, num_windows, m_hop_size, m_window_size);
576 case EnergyMethod::DYNAMIC_RANGE:
577 return D::dynamic_range(data, num_windows, m_hop_size, m_window_size);
578 case EnergyMethod::SPECTRAL:
579 return D::spectral_energy(data, num_windows, m_hop_size, m_window_size);
580 case EnergyMethod::HARMONIC:
581 return D::low_frequency_energy(data, num_windows, m_hop_size, m_window_size);
582 case EnergyMethod::RMS:
584 return D::rms(data, num_windows, m_hop_size, m_window_size);
593 if (data_size < m_window_size)
595 return (data_size - m_window_size) / m_hop_size + 1;
609template <ComputeData InputType = std::vector<Kakshya::DataVariant>>
613template <ComputeData InputType = std::vector<Kakshya::DataVariant>>
644 const auto& ch = analysis.
channels[0];
645 const std::string
q = qualifier.empty() ?
"mean_energy" : qualifier;
647 if (
q ==
"mean_energy")
648 return ch.mean_energy;
649 if (
q ==
"max_energy")
650 return ch.max_energy;
651 if (
q ==
"min_energy")
652 return ch.min_energy;
655 if (
q ==
"dynamic_range")
656 return ch.max_energy - ch.min_energy;
657 if (
q ==
"event_count")
658 return static_cast<double>(ch.event_positions.size());
659 if (
q ==
"window_count")
660 return static_cast<double>(ch.energy_values.size());
662 return ch.mean_energy;
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.
EnergyAnalysis analyze_energy(const input_type &data)
Type-safe energy analysis method.
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)
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.
MAYAFLUX_API double extract_scalar_energy(const EnergyAnalysis &analysis, const std::string &qualifier)
Extract a named scalar from an EnergyAnalysis result.
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::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.