| 97 | } |
| 98 | |
| 99 | void apply_activity_gate_in_place( |
| 100 | std::vector<float> & samples, |
| 101 | const std::vector<float> & reference, |
| 102 | int channels, |
| 103 | int sample_rate, |
| 104 | float threshold_dbfs, |
| 105 | double window_seconds, |
| 106 | double margin_seconds, |
| 107 | double fade_seconds) { |
| 108 | if (channels <= 0 || sample_rate <= 0 || window_seconds <= 0.0 || margin_seconds < 0.0 || fade_seconds < 0.0) { |
| 109 | throw std::runtime_error("audio activity gate received invalid parameters"); |
| 110 | } |
| 111 | if (samples.size() != reference.size() || samples.size() % static_cast<size_t>(channels) != 0) { |
| 112 | throw std::runtime_error("audio activity gate requires matching input and reference shapes"); |
| 113 | } |
| 114 | const int64_t frames = static_cast<int64_t>(samples.size() / static_cast<size_t>(channels)); |
| 115 | if (frames <= 0) { |
| 116 | return; |
| 117 | } |
| 118 | const int64_t window_frames = |
| 119 | std::max<int64_t>(1, static_cast<int64_t>(std::llround(window_seconds * static_cast<double>(sample_rate)))); |
| 120 | const int64_t margin_frames = |
| 121 | std::max<int64_t>(0, static_cast<int64_t>(std::llround(margin_seconds * static_cast<double>(sample_rate)))); |
| 122 | const int64_t fade_frames = |
| 123 | std::max<int64_t>(0, static_cast<int64_t>(std::llround(fade_seconds * static_cast<double>(sample_rate)))); |
| 124 | const double threshold = std::pow(10.0, static_cast<double>(threshold_dbfs) / 20.0); |
| 125 | const double threshold_energy = threshold * threshold; |
| 126 | std::vector<uint8_t> active(static_cast<size_t>(frames), 0); |
| 127 | for (int64_t window_start = 0; window_start < frames; window_start += window_frames) { |
| 128 | const int64_t window_end = std::min(frames, window_start + window_frames); |
| 129 | double energy = 0.0; |
| 130 | int64_t count = 0; |
| 131 | for (int64_t frame = window_start; frame < window_end; ++frame) { |
| 132 | const size_t base = static_cast<size_t>(frame * channels); |
| 133 | for (int channel = 0; channel < channels; ++channel) { |
| 134 | const float sample = reference[base + static_cast<size_t>(channel)]; |
| 135 | energy += static_cast<double>(sample) * static_cast<double>(sample); |
| 136 | ++count; |
| 137 | } |
| 138 | } |
| 139 | const double mean_energy = count > 0 ? energy / static_cast<double>(count) : 0.0; |
| 140 | if (mean_energy > threshold_energy) { |
| 141 | const int64_t begin = std::max<int64_t>(0, window_start - margin_frames); |
| 142 | const int64_t end = std::min<int64_t>(frames, window_end + margin_frames); |
| 143 | std::fill( |
| 144 | active.begin() + static_cast<std::ptrdiff_t>(begin), |
| 145 | active.begin() + static_cast<std::ptrdiff_t>(end), |
| 146 | static_cast<uint8_t>(1)); |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | std::vector<int64_t> prev_active(static_cast<size_t>(frames), -1); |
| 151 | std::vector<int64_t> next_active(static_cast<size_t>(frames), frames); |
| 152 | int64_t prev = -1; |
| 153 | for (int64_t frame = 0; frame < frames; ++frame) { |
| 154 | if (active[static_cast<size_t>(frame)] != 0) { |
| 155 | prev = frame; |
| 156 | } |
no test coverage detected