295 void prepare(
double sample_rate,
int num_channels) {
296 prepare(sample_rate, num_channels,
nullptr);
306 prepare_with_features(sample_rate, num_channels, roles);
319 void prepare(
double sample_rate,
int num_channels,
322 prepare_with_features(sample_rate, num_channels, roles);
326 void prepare_with_features(
double sample_rate,
int num_channels,
328 constexpr double maximum_safe_sample_rate =
329 static_cast<double>(std::numeric_limits<int>::max());
330 const bool sample_rate_supported = std::isfinite(sample_rate)
332 && sample_rate <= maximum_safe_sample_rate;
333 sample_rate_ = sample_rate_supported ? sample_rate : 0.0;
334 const bool channel_count_supported = num_channels > 0
338 sample_rate_supported && channel_count_supported &&
339 configure_channel_roles(roles);
340 loudness_hop_samples_ = loudness_valid_
341 ? std::max(1,
static_cast<int>(std::llround(sample_rate_ * 0.1))) : 0;
343 configure_k_weighting();
344 gate_energy_tree_.assign(kGateBinCount + 1, 0.0);
345 gate_count_tree_.assign(kGateBinCount + 1, 0);
346 gate_node_epoch_.assign(kGateBinCount + 1, 0);
347 lra_count_tree_.assign(kGateBinCount + 1, 0);
348 lra_node_epoch_.assign(kGateBinCount + 1, 0);
350 gate_epoch_active_ =
true;
352 reset_measurement_state(num_channels_);
358 void process(
const SampleType*
const* channels,
int num_channels,
int num_samples) {
359 const int requested_num_channels = num_channels;
360 num_channels = std::clamp(num_channels, 0, num_channels_);
361 if (channels ==
nullptr || num_samples <= 0)
return;
363 for (
int ch = 0; ch < num_channels; ++ch) {
364 if (channels[ch] ==
nullptr) {
370 const bool process_loudness = loudness_valid_
371 && requested_num_channels == num_channels_
372 && num_channels == num_channels_;
377 if (num_channels != active_num_channels_
378 || process_loudness != active_loudness_processing_)
379 reset_measurement_state(num_channels, process_loudness);
381 const bool correlate =
384 while (offset < num_samples) {
385 int piece = num_samples - offset;
386 if (process_loudness)
387 piece = std::min(piece, loudness_hop_samples_ - loudness_hop_position_);
388 if (!measure_piece(channels, num_channels, offset, piece, correlate, process_loudness))
389 measure_piece_sanitized(channels, num_channels, offset, piece, correlate,
392 block_samples_ += piece;
394 correlation_samples_ += piece;
395 if (process_loudness) {
396 loudness_hop_position_ += piece;
397 if (loudness_hop_position_ == loudness_hop_samples_)
398 finish_loudness_hop(num_channels);
404 int block_size =
static_cast<int>(sample_rate_ * 0.01);
405 if (block_size < 1) block_size = 1;
407 if (block_samples_ >= block_size) {
408 emit_snapshot(num_channels);
420 reset_measurement_state(0);
424 friend struct detail::MultiChannelMeterTestAccess;
426 using D2 = detail::lanes::D2;
427 using F4 = detail::lanes::F4;
431 double b0 = 1.0, b1 = 0.0, b2 = 0.0, a1 = 0.0, a2 = 0.0;
434 return {
c.b0,
c.b1,
c.b2,
c.a1,
c.a2};
448 static bool measure_levels(
const SampleType* x,
int n, PieceLevels& out) {
449 namespace lanes = detail::lanes;
453 if constexpr (std::is_same_v<SampleType, float>) {
455 F4 peak4 = lanes::splat(0.0f);
456 auto in_range = lanes::less_equal(peak4, limit);
457 D2 e0 = lanes::splat(0.0), e1 = lanes::splat(0.0);
458 for (;
k + 4 <=
n;
k += 4) {
459 const F4
v = lanes::load(x + k);
460 const F4
a = lanes::abs(v);
461 in_range = lanes::both(in_range, lanes::less_equal(
a, limit));
462 peak4 = lanes::max(peak4,
a);
463 const D2 lo = lanes::widen_low(v);
464 const D2 hi = lanes::widen_high(v);
468 if (!lanes::all(in_range))
470 for (
int lane = 0; lane < 4; ++lane)
471 peak = std::max(
peak,
static_cast<double>(lanes::lane(peak4, lane)));
472 energy = (lanes::lane(e0, 0) + lanes::lane(e1, 0)) +
473 (lanes::lane(e0, 1) + lanes::lane(e1, 1));
476 const double v =
static_cast<double>(
x[
k]);
477 const double a = std::abs(v);
489 static double cross_energy(
const SampleType* x,
const SampleType* y,
int n) {
490 namespace lanes = detail::lanes;
493 if constexpr (std::is_same_v<SampleType, float>) {
494 D2 s0 = lanes::splat(0.0), s1 = lanes::splat(0.0);
495 for (;
k + 4 <=
n;
k += 4) {
496 const F4
a = lanes::load(x + k);
497 const F4
b = lanes::load(y + k);
498 s0 = s0 + lanes::widen_low(
a) * lanes::widen_low(b);
499 s1 = s1 + lanes::widen_high(
a) * lanes::widen_high(b);
501 sum = (lanes::lane(s0, 0) + lanes::lane(s1, 0)) +
502 (lanes::lane(s0, 1) + lanes::lane(s1, 1));
505 sum +=
static_cast<double>(x[k]) *
static_cast<double>(
y[
k]);
511 bool measure_piece(
const SampleType*
const* channels,
int num_channels,
int offset,
int n,
513 std::array<PieceLevels, kMaxMeterChannels>
levels{};
514 for (
int ch = 0; ch < num_channels; ++ch)
515 if (!measure_levels(channels[ch] + offset, n,
levels[
static_cast<std::size_t
>(ch)]))
517 for (
int ch = 0; ch < num_channels; ++ch) {
518 const auto&
l =
levels[
static_cast<std::size_t
>(ch)];
519 block_peak_[ch] = std::max(block_peak_[ch],
l.peak);
521 block_clipped_[ch] =
true;
522 block_sum_sq_[ch] +=
l.energy;
525 correlation_sum_xy_ += cross_energy(channels[0] + offset, channels[1] + offset, n);
526 correlation_sum_xx_ +=
levels[0].energy;
527 correlation_sum_yy_ +=
levels[1].energy;
530 k_weight_piece(channels, num_channels, offset, n);
535 void measure_piece_sanitized(
const SampleType*
const* channels,
int num_channels,
int offset,
536 int n,
bool correlate,
bool loudness) {
537 for (
int i = offset;
i < offset +
n; ++
i) {
538 for (
int ch = 0; ch < num_channels; ++ch) {
541 const double sd = sanitize_sample(channels[ch][
i]);
542 const double abs_s = std::abs(sd);
543 if (abs_s > block_peak_[ch])
544 block_peak_[ch] = abs_s;
546 block_clipped_[ch] =
true;
547 block_sum_sq_[ch] += sd * sd;
549 const double weighted = k_weight(ch, sd);
550 loudness_hop_energy_[ch] += weighted * weighted;
554 const double ld = sanitize_sample(channels[0][
i]);
555 const double rd = sanitize_sample(channels[1][
i]);
556 correlation_sum_xy_ += ld * rd;
557 correlation_sum_xx_ += ld * ld;
558 correlation_sum_yy_ += rd * rd;
566 void k_weight_piece(
const SampleType*
const* channels,
int num_channels,
int offset,
int n) {
567 namespace lanes = detail::lanes;
569 for (; ch + 2 <= num_channels; ch += 2) {
570 const SampleType*
a = channels[ch] + offset;
571 const SampleType*
b = channels[ch + 1] + offset;
572 const auto u =
static_cast<std::size_t
>(ch);
573 auto pair = [&](
const std::array<double, kMaxMeterChannels>&
v) {
574 return lanes::make(v[
u], v[
u + 1]);
576 D2
x1 = pair(k_x1_),
x2 = pair(k_x2_);
577 D2 y1 = pair(k_y1_), y2 = pair(k_y2_);
578 D2 w1 = pair(k_w1_), w2 = pair(k_w2_);
579 D2 even = lanes::splat(0.0), odd = lanes::splat(0.0);
580 const Section& sc = shelf_;
581 const Section& hc = high_pass_;
583 auto step = [&](D2
x) {
584 const D2
y = section(sc, x, x1, x2, y1, y2);
585 const D2
w = section(hc, y, y1, y2, w1, w2);
594 for (;
k + 2 <=
n;
k += 2) {
596 step(lanes::make(
static_cast<double>(
a[k]),
static_cast<double>(b[k])));
597 even = even + w0 * w0;
599 step(lanes::make(
static_cast<double>(
a[k + 1]),
static_cast<double>(b[k + 1])));
600 odd = odd + w1k * w1k;
604 step(lanes::make(
static_cast<double>(
a[k]),
static_cast<double>(b[k])));
605 even = even + w0 * w0;
607 const D2
energy = even + odd;
608 store_pair(k_x1_,
u, x1);
609 store_pair(k_x2_,
u, x2);
610 store_pair(k_y1_,
u, y1);
611 store_pair(k_y2_,
u, y2);
612 store_pair(k_w1_,
u, w1);
613 store_pair(k_w2_,
u, w2);
614 loudness_hop_energy_[
u] += lanes::lane(
energy, 0);
615 loudness_hop_energy_[
u + 1] += lanes::lane(
energy, 1);
617 for (; ch < num_channels; ++ch) {
618 const SampleType*
a = channels[ch] + offset;
620 for (
int k = 0;
k <
n; ++
k) {
621 const double w = k_weight(ch,
static_cast<double>(
a[k]));
624 loudness_hop_energy_[ch] +=
energy;
631 template <
typename T>
static T section(
const Section&
c, T x, T x1, T x2, T y1, T y2) {
632 if constexpr (std::is_same_v<T, double>) {
633 const double head =
c.b0 *
x +
c.b1 *
x1 +
c.b2 *
x2 -
c.a2 * y2;
634 return head -
c.a1 * y1;
636 namespace lanes = detail::lanes;
637 const T head = lanes::splat(
c.b0) *
x + lanes::splat(
c.b1) *
x1 +
638 lanes::splat(
c.b2) *
x2 - lanes::splat(
c.a2) * y2;
639 return head - lanes::splat(
c.a1) * y1;
643 static void store_pair(std::array<double, kMaxMeterChannels>& v, std::size_t
u, D2 x) {
644 v[
u] = detail::lanes::lane(x, 0);
645 v[
u + 1] = detail::lanes::lane(x, 1);
648 void reset_measurement_state(
int active_channels,
649 bool process_loudness =
false) {
651 block_peak_[ch] = 0.0f;
652 block_sum_sq_[ch] = 0.0f;
653 block_clipped_[ch] =
false;
656 correlation_sum_xy_ = 0.0;
657 correlation_sum_xx_ = 0.0;
658 correlation_sum_yy_ = 0.0;
659 correlation_samples_ = 0;
660 reset_loudness_state();
662 active_num_channels_ = active_channels;
663 active_loudness_processing_ = process_loudness;
664 snapshot_.num_channels = active_channels;
667 void reset_correlation_accumulators() {
668 correlation_sum_xy_ = 0.0;
669 correlation_sum_xx_ = 0.0;
670 correlation_sum_yy_ = 0.0;
671 correlation_samples_ = 0;
674 void emit_snapshot(
int num_channels) {
675 snapshot_.num_channels = num_channels;
677 for (
int ch = 0; ch < num_channels; ++ch) {
678 auto& out = snapshot_.channels[ch];
679 out.peak =
static_cast<float>(block_peak_[ch]);
680 out.rms = block_samples_ > 0
681 ?
static_cast<float>(std::sqrt(block_sum_sq_[ch] / block_samples_))
683 out.clipped = block_clipped_[ch];
685 out.lufs_momentary = channel_momentary_[ch];
688 block_peak_[ch] = 0.0f;
689 block_sum_sq_[ch] = 0.0f;
690 block_clipped_[ch] =
false;
694 if (num_channels >= 2 && correlation_samples_ > 0) {
695 double denom = std::sqrt(correlation_sum_xx_ * correlation_sum_yy_);
696 snapshot_.correlation = denom > 1
e-10
697 ?
static_cast<float>(correlation_sum_xy_ / denom)
700 snapshot_.correlation = 0.0f;
704 int corr_window =
static_cast<int>(sample_rate_ * 0.1);
705 if (correlation_samples_ >= corr_window) {
706 reset_correlation_accumulators();
712 static constexpr double kAbsoluteGateLufs = -70.0;
717 static constexpr double kGateMaximumLufs = 140.0;
718 static constexpr double kGateBinWidthLu = 0.01;
719 static constexpr std::size_t kGateBinCount = 21001;
720 static constexpr double kGateMaximumEnergy = 1.0e14;
721 static constexpr std::size_t kShortTermHopCount = 30;
725 double k_weight(
int channel,
double x) {
726 const auto c =
static_cast<std::size_t
>(channel);
727 const double y = section(shelf_, x, k_x1_[
c], k_x2_[
c], k_y1_[
c], k_y2_[
c]);
728 const double w = section(high_pass_, y, k_y1_[
c], k_y2_[
c], k_w1_[
c], k_w2_[
c]);
738 static double sanitize_sample(SampleType sample) {
739 const double value =
static_cast<double>(sample);
740 if (!std::isfinite(value))
return 0.0;
745 static float energy_to_lufs(
double energy) {
747 ?
static_cast<float>(-0.691 + 10.0 * std::log10(
energy))
748 : -std::numeric_limits<float>::infinity();
774 channel_weights_.fill(0.0);
775 if (num_channels_ < 1)
return false;
777 constexpr std::array<LoudnessChannelRole, 8> eight_channel_default{
782 std::array<LoudnessChannelRole, kMaxMeterChannels>
configured{};
785 for (
int ch = 0; ch < num_channels_; ++ch) {
786 const auto role = roles[ch];
787 configured[
static_cast<std::size_t
>(ch)] = role;
790 switch (num_channels_) {
799 std::copy_n(eight_channel_default.begin(), 3,
configured.begin());
813 std::copy_n(eight_channel_default.begin(), 6,
configured.begin());
816 std::copy(eight_channel_default.begin(),
817 eight_channel_default.end(),
configured.begin());
824 bool has_program_channel =
false;
825 for (
int ch = 0; ch < num_channels_; ++ch) {
827 if (!role_weight(
configured[
static_cast<std::size_t
>(ch)], weight))
return false;
828 channel_weights_[
static_cast<std::size_t
>(ch)] = weight;
829 has_program_channel = has_program_channel || weight > 0.0;
831 return has_program_channel;
834 void configure_k_weighting() {
837 reset_k_weighting_state();
839 if (!loudness_valid_ || !coefficients.valid) {
840 loudness_valid_ =
false;
843 shelf_ = Section::from(coefficients.shelf);
844 high_pass_ = Section::from(coefficients.high_pass);
847 void reset_k_weighting_state() {
848 for (
auto* v : {&k_x1_, &k_x2_, &k_y1_, &k_y2_, &k_w1_, &k_w2_})
852 void reset_loudness_state() {
853 reset_k_weighting_state();
854 loudness_hop_energy_.fill(0.0);
855 for (
auto& hop : loudness_energy_ring_) hop.fill(0.0);
856 loudness_short_term_ring_.fill(0.0);
857 channel_momentary_.fill(-std::numeric_limits<float>::infinity());
858 loudness_hop_position_ = 0;
859 loudness_hops_completed_ = 0;
860 loudness_ring_position_ = 0;
861 loudness_short_term_position_ = 0;
862 snapshot_.lufs_momentary = -std::numeric_limits<float>::infinity();
863 snapshot_.lufs_integrated = -std::numeric_limits<float>::infinity();
864 snapshot_.short_term_lufs = -std::numeric_limits<float>::infinity();
865 snapshot_.short_term_valid =
false;
866 snapshot_.loudness_range_lu = 0.0f;
867 snapshot_.loudness_range_valid =
false;
868 gate_nodes_initialized_ = 0;
872 if (gate_epoch_ == std::numeric_limits<std::uint64_t>::max())
873 gate_epoch_active_ =
false;
878 void finish_loudness_hop(
int num_channels) {
879 double hop_program_energy = 0.0;
880 for (
int ch = 0; ch < num_channels; ++ch)
881 hop_program_energy += channel_weights_[ch] * loudness_hop_energy_[ch]
882 /
static_cast<double>(loudness_hop_samples_);
883 loudness_short_term_ring_[loudness_short_term_position_] = std::min(
884 hop_program_energy, kGateMaximumEnergy);
885 loudness_short_term_position_ = (loudness_short_term_position_ + 1) % kShortTermHopCount;
886 loudness_energy_ring_[loudness_ring_position_] = loudness_hop_energy_;
887 loudness_hop_energy_.fill(0.0);
888 loudness_hop_position_ = 0;
889 loudness_ring_position_ = (loudness_ring_position_ + 1) % 4;
890 ++loudness_hops_completed_;
891 if (loudness_hops_completed_ < 4)
return;
893 double program_energy = 0.0;
894 for (
int ch = 0; ch < num_channels; ++ch) {
895 double channel_energy = 0.0;
896 for (
const auto& hop : loudness_energy_ring_) channel_energy += hop[ch];
897 channel_energy /=
static_cast<double>(4 * loudness_hop_samples_);
898 const double weighted_energy = std::min(
899 channel_weights_[ch] * channel_energy, kGateMaximumEnergy);
900 channel_momentary_[ch] = energy_to_lufs(weighted_energy);
901 program_energy = std::min(
902 program_energy + weighted_energy, kGateMaximumEnergy);
904 snapshot_.lufs_momentary = energy_to_lufs(program_energy);
906 if (energy_to_lufs(program_energy) > kAbsoluteGateLufs) {
907 add_gating_block(program_energy);
908 update_integrated_loudness();
910 update_short_term_and_range();
913 void add_gating_block(
double energy) {
914 if (!gate_epoch_active_)
return;
916 const double clamped = std::clamp(
loudness, kAbsoluteGateLufs, kGateMaximumLufs);
917 const auto bin = std::min(kGateBinCount - 1,
static_cast<std::size_t
>(
918 (clamped - kAbsoluteGateLufs) / kGateBinWidthLu));
919 for (std::size_t index = bin + 1;
index <= kGateBinCount;
920 index += low_bit(index)) {
921 if (gate_node_epoch_[index] != gate_epoch_) {
922 gate_node_epoch_[
index] = gate_epoch_;
923 gate_energy_tree_[
index] = 0.0;
924 gate_count_tree_[
index] = 0;
925 ++gate_nodes_initialized_;
928 ++gate_count_tree_[
index];
932 std::pair<double, std::uint64_t> gating_blocks_from(std::size_t first_bin)
const {
933 if (!gate_epoch_active_)
return {0.0, 0};
934 double prefix_energy = 0.0;
935 std::uint64_t prefix_count = 0;
936 for (std::size_t index = first_bin;
index > 0;
index -= low_bit(index)) {
937 if (gate_node_epoch_[index] == gate_epoch_) {
938 prefix_energy += gate_energy_tree_[
index];
939 prefix_count += gate_count_tree_[
index];
942 double total_energy = 0.0;
943 std::uint64_t total_count = 0;
944 for (std::size_t index = kGateBinCount;
index > 0;
index -= low_bit(index)) {
945 if (gate_node_epoch_[index] == gate_epoch_) {
946 total_energy += gate_energy_tree_[
index];
947 total_count += gate_count_tree_[
index];
950 return {total_energy - prefix_energy, total_count - prefix_count};
953 static constexpr std::size_t low_bit(std::size_t value) {
954 return value & (~value + 1);
957 void update_integrated_loudness() {
958 const auto [absolute_sum, absolute_count] = gating_blocks_from(0);
959 if (absolute_count == 0) {
960 snapshot_.lufs_integrated = -std::numeric_limits<float>::infinity();
963 const double relative_gate = energy_to_lufs(absolute_sum / absolute_count) - 10.0;
964 const auto first_bin = relative_gate <= kAbsoluteGateLufs ? 0
965 : std::min(kGateBinCount,
static_cast<std::size_t
>(
966 (relative_gate - kAbsoluteGateLufs) / kGateBinWidthLu) + 1);
967 const auto [gated_sum, gated_count] = gating_blocks_from(first_bin);
968 snapshot_.lufs_integrated = gated_count > 0
969 ? energy_to_lufs(gated_sum / gated_count)
970 : -std::numeric_limits<float>::infinity();
973 void update_short_term_and_range() {
974 if (loudness_hops_completed_ < kShortTermHopCount)
return;
976 for (
const double hop : loudness_short_term_ring_)
energy += hop;
977 snapshot_.short_term_lufs = energy_to_lufs(
energy / kShortTermHopCount);
978 snapshot_.short_term_valid = std::isfinite(snapshot_.short_term_lufs);
979 if (!snapshot_.short_term_valid)
return;
980 if (snapshot_.short_term_lufs > kAbsoluteGateLufs)
981 add_lra_block(snapshot_.short_term_lufs);
982 update_loudness_range();
985 void add_lra_block(
double loudness) {
986 const auto bin = std::min(kGateBinCount - 1,
static_cast<std::size_t
>(
987 (std::clamp(
loudness, kAbsoluteGateLufs, kGateMaximumLufs)
988 - kAbsoluteGateLufs) / kGateBinWidthLu));
989 for (std::size_t index = bin + 1;
index <= kGateBinCount;
index += low_bit(index)) {
990 if (lra_node_epoch_[index] != gate_epoch_) {
991 lra_node_epoch_[
index] = gate_epoch_;
992 lra_count_tree_[
index] = 0;
994 ++lra_count_tree_[
index];
998 std::uint64_t lra_prefix_count(std::size_t exclusive_bin)
const {
999 std::uint64_t
count = 0;
1000 for (std::size_t index = exclusive_bin;
index > 0;
index -= low_bit(index))
1001 if (lra_node_epoch_[index] == gate_epoch_)
count += lra_count_tree_[
index];
1005 float lra_value_at_rank(std::size_t first_bin, std::uint64_t rank)
const {
1006 const std::uint64_t target = lra_prefix_count(first_bin) + rank;
1007 std::size_t
low = 0,
high = kGateBinCount - 1;
1008 while (low < high) {
1010 if (lra_prefix_count(mid + 1) >= target)
high = mid;
1013 return static_cast<float>(kAbsoluteGateLufs +
low * kGateBinWidthLu);
1016 void update_loudness_range() {
1017 if (!std::isfinite(snapshot_.lufs_integrated))
return;
1018 const double gate = std::max(kAbsoluteGateLufs,
1019 static_cast<double>(snapshot_.lufs_integrated) - 20.0);
1020 const auto first_bin = gate <= kAbsoluteGateLufs ? 0 : std::min(kGateBinCount,
1021 static_cast<std::size_t
>((gate - kAbsoluteGateLufs) / kGateBinWidthLu) + 1);
1022 const auto count = lra_prefix_count(kGateBinCount) - lra_prefix_count(first_bin);
1023 if (count == 0)
return;
1024 const auto p10 = std::max<std::uint64_t>(1,
static_cast<std::uint64_t
>(std::ceil(count * 0.10)));
1025 const auto p95 = std::max<std::uint64_t>(1,
static_cast<std::uint64_t
>(std::ceil(count * 0.95)));
1026 snapshot_.loudness_range_lu = lra_value_at_rank(first_bin, p95)
1027 - lra_value_at_rank(first_bin, p10);
1028 snapshot_.loudness_range_valid =
true;
1031 double sample_rate_ = 44100.0;
1032 int num_channels_ = 2;
1039 int block_samples_ = 0;
1041 bool loudness_valid_ =
false;
1042 int loudness_hop_samples_ = 4410;
1043 int loudness_hop_position_ = 0;
1044 std::uint64_t loudness_hops_completed_ = 0;
1045 std::size_t loudness_ring_position_ = 0;
1046 int active_num_channels_ = 0;
1047 bool active_loudness_processing_ =
false;
1049 Section high_pass_{};
1052 std::array<double, kMaxMeterChannels> k_x1_{};
1053 std::array<double, kMaxMeterChannels> k_x2_{};
1054 std::array<double, kMaxMeterChannels> k_y1_{};
1055 std::array<double, kMaxMeterChannels> k_y2_{};
1056 std::array<double, kMaxMeterChannels> k_w1_{};
1057 std::array<double, kMaxMeterChannels> k_w2_{};
1058 std::array<double, kMaxMeterChannels> channel_weights_{};
1059 std::array<double, kMaxMeterChannels> loudness_hop_energy_{};
1060 std::array<std::array<double, kMaxMeterChannels>, 4> loudness_energy_ring_{};
1061 std::array<double, kShortTermHopCount> loudness_short_term_ring_{};
1062 std::size_t loudness_short_term_position_ = 0;
1063 std::array<float, kMaxMeterChannels> channel_momentary_{};
1066 double correlation_sum_xy_ = 0.0;
1067 double correlation_sum_xx_ = 0.0;
1068 double correlation_sum_yy_ = 0.0;
1069 int correlation_samples_ = 0;
1071 std::vector<double> gate_energy_tree_;
1072 std::vector<std::uint64_t> gate_count_tree_;
1073 std::vector<std::uint64_t> gate_node_epoch_;
1074 std::vector<std::uint64_t> lra_count_tree_;
1075 std::vector<std::uint64_t> lra_node_epoch_;
1076 std::uint64_t gate_epoch_ = 0;
1077 std::size_t gate_nodes_initialized_ = 0;
1078 bool gate_epoch_active_ =
true;
1080 MultiChannelMeterData snapshot_;