Zrythm v2.0.0-DEV
a highly automated and intuitive digital audio workstation
Loading...
Searching...
No Matches
mock_hardware_audio_interface_threaded.h
1// SPDX-FileCopyrightText: © 2026 Alexandros Theodotou <alex@zrythm.org>
2// SPDX-License-Identifier: LicenseRef-ZrythmLicense
3
4#pragma once
5
6#include <atomic>
7#include <thread>
8
9#include "dsp/hardware_audio_interface.h"
10#include "dsp/iaudio_callback.h"
11#include "utils/units.h"
12
13namespace zrythm::test_helpers
14{
15
22class ThreadedMockHardwareAudioInterface : public dsp::IHardwareAudioInterface
23{
24public:
25 explicit ThreadedMockHardwareAudioInterface (
26 units::sample_rate_t sample_rate = units::sample_rate (48000),
27 units::sample_u32_t block_length = units::samples (256),
28 units::channel_count_t input_channels = units::channels (2),
29 units::channel_count_t output_channels = units::channels (2))
30 : device_info_{
31 .device_name = {},
32 .sample_rate = sample_rate,
33 .block_length = block_length,
34 .input_channel_count = input_channels,
35 .output_channel_count = output_channels,
36 }
37 {
38 }
39
40 ~ThreadedMockHardwareAudioInterface () override { stop (); }
41
42 [[nodiscard]] dsp::AudioDeviceInfo get_device_info () const override
43 {
44 return device_info_;
45 }
46
54 {
55 assert (!processing_active_.load (std::memory_order_acquire));
56 device_info_ = std::move (info);
57 }
58
65 [[nodiscard]] std::size_t process_call_count () const
66 {
67 return process_call_count_.load (std::memory_order_acquire);
68 }
69
70 void add_audio_callback (dsp::IAudioCallback * callback) override
71 {
72 stop ();
73 auto * old_cb = callback_.load (std::memory_order_acquire);
74 if (old_cb != nullptr)
75 {
76 old_cb->stopped ();
77 }
78 if (callback != nullptr)
79 {
80 callback_.store (callback, std::memory_order_release);
81 start_callback_thread ();
82 }
83 }
84
85 void remove_audio_callback (dsp::IAudioCallback * callback) override
86 {
87 stop ();
88 auto * cb = callback_.load (std::memory_order_acquire);
89 if (cb != nullptr)
90 {
91 cb->stopped ();
92 }
93 callback_.store (nullptr, std::memory_order_release);
94 processing_active_.store (false, std::memory_order_release);
95 }
96
97 void simulate_device_change (dsp::AudioDeviceInfo new_info)
98 {
99 stop ();
100 auto * cb = callback_.load (std::memory_order_acquire);
101 if (cb != nullptr)
102 {
103 cb->stopped ();
104 processing_active_.store (false, std::memory_order_release);
105 device_info_ = std::move (new_info);
106 start_callback_thread ();
107 }
108 }
109
110private:
111 void start_callback_thread ()
112 {
113 auto * cb = callback_.load (std::memory_order_acquire);
114 cb->about_to_start ();
115 processing_active_.store (true, std::memory_order_release);
116
117 is_running_.store (true, std::memory_order_release);
118 callback_thread_ = std::jthread ([this] (std::stop_token stoken) {
119 const auto num_channels =
120 device_info_.input_channel_count.in<int> (units::channels);
121 const auto bl = device_info_.block_length.in (units::samples);
122 std::vector<float> output_buf (bl * num_channels, 0.f);
123 std::vector<float> input_buf (bl * num_channels, 0.f);
124 std::vector<float *> output_ptrs (num_channels);
125 std::vector<const float *> input_ptrs (num_channels);
126 for (int ch = 0; ch < num_channels; ++ch)
127 {
128 output_ptrs[ch] = output_buf.data () + (static_cast<size_t> (ch) * bl);
129 input_ptrs[ch] = input_buf.data () + (static_cast<size_t> (ch) * bl);
130 }
131
132 while (
133 !stoken.stop_requested ()
134 && is_running_.load (std::memory_order_acquire))
135 {
136 auto * current_cb = callback_.load (std::memory_order_acquire);
137 if (current_cb)
138 {
139 current_cb->process_audio (
140 { input_ptrs.data (), static_cast<size_t> (num_channels) },
141 { output_ptrs.data (), static_cast<size_t> (num_channels) },
142 device_info_.block_length);
143 process_call_count_.fetch_add (1, std::memory_order_acq_rel);
144 }
145 auto sleep_us = static_cast<int64_t> (
146 device_info_.block_length.in<double> (units::samples) * 1'000'000.0
147 / device_info_.sample_rate.in (units::sample_rate));
148 std::this_thread::sleep_for (std::chrono::microseconds (sleep_us));
149 }
150 });
151 }
152
153 void stop ()
154 {
155 is_running_.store (false, std::memory_order_release);
156 if (callback_thread_.joinable ())
157 {
158 callback_thread_.request_stop ();
159 callback_thread_.join ();
160 }
161 }
162
163 dsp::AudioDeviceInfo device_info_;
164 std::atomic<bool> processing_active_{ false };
165 std::atomic<dsp::IAudioCallback *> callback_{ nullptr };
166 std::atomic<bool> is_running_{ false };
167 std::atomic<std::size_t> process_call_count_{ 0 };
168 std::jthread callback_thread_;
169};
170
171} // namespace zrythm::test_helpers
Pure-abstract audio callback interface.
Abstraction for hardware audio interface.
void remove_audio_callback(dsp::IAudioCallback *callback) override
Removes a previously added audio callback.
void add_audio_callback(dsp::IAudioCallback *callback) override
Adds an audio callback to receive audio I/O events.
std::size_t process_call_count() const
Returns the number of times process_audio has been called.
void set_device_info(dsp::AudioDeviceInfo info)
Updates the device info.
dsp::AudioDeviceInfo get_device_info() const override
Returns the current audio device information.
Information about an audio device.