Line data Source code
1 : #include <algorithm>
2 : #include <cassert>
3 : #include <chrono>
4 : #include <cmath>
5 : #include <cstring>
6 :
7 : #include "audio/engine/analyzer_capture.h"
8 : #include "audio/engine/audio_engine.h"
9 :
10 : namespace Amplitron {
11 :
12 4625 : void AudioEngine::process_audio(const float* input, float* output, int frame_count) {
13 4625 : auto t_start = std::chrono::steady_clock::now();
14 :
15 4625 : if (frame_count > static_cast<int>(process_buffer_.size())) {
16 0 : process_buffer_.resize(frame_count, 0.0f);
17 0 : process_buffer_right_.resize(frame_count, 0.0f);
18 0 : }
19 :
20 4625 : const bool analyzer_on = analyzer_capture_->is_analyzer_enabled();
21 :
22 4625 : float in_gain = input_gain_.load(std::memory_order_relaxed);
23 4625 : float peak_in = 0.0f;
24 4625 : if (analyzer_on) {
25 16 : float sum_sq_in = 0.0f;
26 16 : bool clipped_in = false;
27 7320 : for (int i = 0; i < frame_count; ++i) {
28 7296 : process_buffer_[i] = input[i] * in_gain;
29 7296 : float abs_val = std::fabs(process_buffer_[i]);
30 7296 : if (abs_val > peak_in) peak_in = abs_val;
31 7296 : if (abs_val >= 1.0f) clipped_in = true;
32 7296 : sum_sq_in += process_buffer_[i] * process_buffer_[i];
33 2432 : }
34 24 : input_rms_.store(std::sqrt(sum_sq_in / std::max(frame_count, 1)),
35 : std::memory_order_relaxed);
36 24 : if (clipped_in) input_clipped_.store(true, std::memory_order_release);
37 24 : analyzer_capture_->capture_input(process_buffer_.data(), frame_count);
38 8 : } else {
39 299129 : for (int i = 0; i < frame_count; ++i) {
40 294528 : process_buffer_[i] = input[i] * in_gain;
41 294528 : float abs_val = std::fabs(process_buffer_[i]);
42 294528 : if (abs_val > peak_in) peak_in = abs_val;
43 148672 : }
44 : }
45 4625 : input_level_.store(peak_in);
46 :
47 6956 : std::memcpy(process_buffer_right_.data(), process_buffer_.data(),
48 4625 : static_cast<size_t>(frame_count) * sizeof(float));
49 :
50 4625 : command_dispatcher_.drain_gain_commands(input_gain_, output_gain_, audio_shadow_executor_);
51 :
52 4625 : if (effect_mutex_.try_lock()) {
53 6942 : command_dispatcher_.drain_commands(input_gain_, output_gain_, audio_shadow_executor_,
54 4618 : main_graph_, dummy_effects_);
55 4618 : if (topology_dirty_.exchange(false, std::memory_order_acq_rel)) {
56 52 : audio_shadow_executor_ = main_executor_;
57 52 : audio_shadow_tuner_ = tuner_tap_;
58 22 : }
59 4618 : effect_mutex_.unlock();
60 2324 : }
61 :
62 4625 : if (audio_shadow_tuner_ && audio_shadow_tuner_->is_enabled()) {
63 3 : audio_shadow_tuner_->process(process_buffer_.data(), frame_count);
64 4 : std::memcpy(process_buffer_right_.data(), process_buffer_.data(),
65 3 : static_cast<size_t>(frame_count) * sizeof(float));
66 1 : }
67 : // The executor handles all the looping, routing, and processing internally!
68 4625 : if (audio_shadow_executor_) {
69 : // Broadcast tempo/bpm
70 4625 : audio_shadow_executor_->update_transport_state(static_cast<float>(metronome_->get_bpm()));
71 :
72 : // Pass your mono/stereo buffers to the executor we built
73 6956 : audio_shadow_executor_->process(process_buffer_.data(), process_buffer_right_.data(),
74 2331 : frame_count);
75 6956 : std::memcpy(process_buffer_.data(), process_buffer_right_.data(),
76 4625 : static_cast<size_t>(frame_count) * sizeof(float));
77 2331 : }
78 :
79 4625 : float out_gain = output_gain_.load(std::memory_order_relaxed);
80 4625 : float peak_out = 0.0f;
81 305302 : auto next_metronome_sample = [this]() -> float { return metronome_->next_sample(); };
82 4625 : if (analyzer_on) {
83 16 : float sum_sq_out = 0.0f;
84 16 : bool clipped_out = false;
85 7320 : for (int i = 0; i < frame_count; ++i) {
86 7296 : float click = next_metronome_sample();
87 7296 : float out_l = clamp(process_buffer_[i] * out_gain + click, -1.0f, 1.0f);
88 7296 : float out_r = clamp(process_buffer_right_[i] * out_gain + click, -1.0f, 1.0f);
89 7296 : if (std::fabs(out_l) >= 1.0f || std::fabs(out_r) >= 1.0f) clipped_out = true;
90 7296 : output[i * 2] = out_l;
91 7296 : output[i * 2 + 1] = out_r;
92 7296 : process_buffer_[i] = out_l;
93 7296 : float abs_val = std::fabs(out_l);
94 7296 : if (abs_val > peak_out) peak_out = abs_val;
95 7296 : sum_sq_out += out_l * out_l;
96 2432 : }
97 24 : output_rms_.store(std::sqrt(sum_sq_out / std::max(frame_count, 1)),
98 : std::memory_order_relaxed);
99 24 : if (clipped_out) output_clipped_.store(true, std::memory_order_release);
100 24 : analyzer_capture_->capture_output(process_buffer_.data(), frame_count);
101 8 : } else {
102 299129 : for (int i = 0; i < frame_count; ++i) {
103 294528 : float click = next_metronome_sample();
104 294528 : float out_l = clamp(process_buffer_[i] * out_gain + click, -1.0f, 1.0f);
105 294528 : float out_r = clamp(process_buffer_right_[i] * out_gain + click, -1.0f, 1.0f);
106 294528 : output[i * 2] = out_l;
107 294528 : output[i * 2 + 1] = out_r;
108 294528 : process_buffer_[i] = out_l;
109 294528 : float abs_val = std::fabs(out_l);
110 294528 : if (abs_val > peak_out) peak_out = abs_val;
111 148672 : }
112 : }
113 4625 : output_level_.store(peak_out);
114 :
115 4625 : if (recorder_->is_recording()) {
116 4 : recorder_->write_samples_stereo(process_buffer_.data(), process_buffer_right_.data(),
117 1 : frame_count);
118 1 : }
119 :
120 4625 : auto t_end = std::chrono::steady_clock::now();
121 4625 : float duration_us = std::chrono::duration<float, std::micro>(t_end - t_start).count();
122 4625 : callback_duration_us_.store(duration_us, std::memory_order_relaxed);
123 4625 : float budget_us = (static_cast<float>(frame_count) / sample_rate_) * 1e6f;
124 4625 : cpu_load_.store(duration_us / budget_us, std::memory_order_relaxed);
125 4625 : }
126 :
127 : } // namespace Amplitron
|