• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ossia / libossia / 36044633724

24 Sep 2026 06:55PM UTC coverage: 53.393% (+1.4%) from 52.04%
36044633724

Pull #950

github

web-flow
Merge f9e87ad5e into 8a33000f7
Pull Request #950: Mixer: gain stages, telemetry arena, port changes between ticks

988 of 1070 new or added lines in 21 files covered. (92.34%)

15 existing lines in 5 files now uncovered.

32942 of 61697 relevant lines covered (53.39%)

181991.06 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.83
/tests/Dataflow/TelemetryTest.cpp
1
// The telemetry arena hands meter levels from the audio thread to the
2
// interface without losing a peak, whatever the reading rate.
3

4
#include <ossia/detail/config.hpp>
5

6
#include <ossia/audio/audio_parameter.hpp>
7
#include <ossia/audio/audio_protocol.hpp>
8
#include <ossia/dataflow/execution_state.hpp>
9
#include <ossia/dataflow/port.hpp>
10
#include <ossia/dataflow/telemetry.hpp>
11
#include <ossia/network/generic/generic_device.hpp>
12

13
#include <catch2/catch_approx.hpp>
14
#include <catch2/catch_test_macros.hpp>
15

16
#include <array>
17
#include <memory>
18

19
using namespace ossia::telemetry;
20

21
namespace
22
{
23
constexpr std::size_t frames = 8;
24

25
ossia::audio_vector constant(std::initializer_list<double> levels)
16 ✔
26
{
27
  ossia::audio_vector v;
16 ✔
28
  for(double l : levels)
38 ✔
29
  {
30
    ossia::audio_channel c;
22 ✔
31
    c.assign(frames, l);
22 ✔
32
    v.push_back(std::move(c));
22 ✔
33
  }
22 ✔
34
  return v;
16 ✔
NEW
35
}
×
36

37
struct Fixture
38
{
39
  arena a{4};
40
  std::shared_ptr<meter_tap> tap = std::make_shared<meter_tap>();
41
  std::weak_ptr<meter_tap> weak = tap;
42

43
  Fixture()
9 ✔
44
  {
9 ✔
45
    a.set_publish_interval(frames);
9 ✔
46
    a.attach(1, 7, tap_kind::node, tap);
9 ✔
47
    // The arena holds the tap now; the caller got the previous one back.
48
    REQUIRE(!tap);
9 ✔
49
    REQUIRE(!weak.expired());
9 ✔
50
  }
9 ✔
51

52
  // One tick in which the tap's writer carries `v`.
53
  void tick(const ossia::audio_vector& v)
17 ✔
54
  {
55
    weak.lock()->pending.accumulate(v);
17 ✔
56
    a.tick(frames, 48000);
17 ✔
57
  }
17 ✔
58

59
  const meter_slot& slot() { return a.latest().meters[1]; }
19 ✔
60
};
61
}
62

63
TEST_CASE("A published frame carries the tap's levels", "[telemetry]")
1 ✔
64
{
65
  Fixture f;
1 ✔
66
  f.tick(constant({0.5, -0.25}));
1 ✔
67

68
  REQUIRE(f.a.consume());
1 ✔
69
  const auto& s = f.slot();
1 ✔
70
  CHECK(s.generation == 7);
1 ✔
71
  CHECK(s.levels.channels == 2);
1 ✔
72
  CHECK(s.levels.ticks == 1);
1 ✔
73
  CHECK(s.levels.frames == frames);
1 ✔
74
  CHECK(s.levels.peak[0] == Catch::Approx(0.5));
1 ✔
75
  CHECK(s.levels.peak[1] == Catch::Approx(0.25));
1 ✔
76
  CHECK(s.levels.rms(0) == Catch::Approx(0.5));
1 ✔
77
  CHECK(!s.levels.is_clipped(0));
1 ✔
78

79
  CHECK(!f.a.consume());
1 ✔
80
}
1 ✔
81

82
TEST_CASE("Nothing is published before the interval has elapsed", "[telemetry]")
1 ✔
83
{
84
  Fixture f;
1 ✔
85
  f.a.set_publish_interval(3 * frames);
1 ✔
86
  f.tick(constant({0.1}));
1 ✔
87
  f.tick(constant({0.9}));
1 ✔
88
  CHECK(!f.a.consume());
1 ✔
89

90
  f.tick(constant({0.2}));
1 ✔
91
  REQUIRE(f.a.consume());
1 ✔
92
  CHECK(f.slot().levels.peak[0] == Catch::Approx(0.9));
1 ✔
93
  CHECK(f.slot().levels.ticks == 3);
1 ✔
94
}
1 ✔
95

96
TEST_CASE("A frame nobody read is folded into the next one", "[telemetry]")
1 ✔
97
{
98
  Fixture f;
1 ✔
99
  f.tick(constant({0.9}));
1 ✔
100
  f.tick(constant({0.1}));
1 ✔
101
  f.tick(constant({0.2}));
1 ✔
102

103
  REQUIRE(f.a.consume());
1 ✔
104
  CHECK(f.slot().levels.peak[0] == Catch::Approx(0.9));
1 ✔
105
  CHECK(f.slot().levels.ticks == 3);
1 ✔
106
}
1 ✔
107

108
TEST_CASE("A frame that was read is not counted again", "[telemetry]")
1 ✔
109
{
110
  Fixture f;
1 ✔
111
  f.tick(constant({0.9}));
1 ✔
112
  REQUIRE(f.a.consume());
1 ✔
113

114
  f.tick(constant({0.1}));
1 ✔
115
  REQUIRE(f.a.consume());
1 ✔
116
  CHECK(f.slot().levels.peak[0] == Catch::Approx(0.1));
1 ✔
117
  CHECK(f.slot().levels.ticks == 1);
1 ✔
118
}
1 ✔
119

120
TEST_CASE("A clip stays flagged until read", "[telemetry]")
1 ✔
121
{
122
  Fixture f;
1 ✔
123
  f.tick(constant({0.1, 1.5}));
1 ✔
124
  f.tick(constant({0.1, 0.1}));
1 ✔
125
  REQUIRE(f.a.consume());
1 ✔
126
  CHECK(!f.slot().levels.is_clipped(0));
1 ✔
127
  CHECK(f.slot().levels.is_clipped(1));
1 ✔
128
}
1 ✔
129

130
TEST_CASE("A signal that widens and narrows keeps its widest count", "[telemetry]")
1 ✔
131
{
132
  Fixture f;
1 ✔
133
  f.tick(constant({0.1}));
1 ✔
134
  f.tick(constant({0.1, 0.2, 0.3}));
1 ✔
135
  f.tick(constant({0.4}));
1 ✔
136
  REQUIRE(f.a.consume());
1 ✔
137
  CHECK(f.slot().levels.channels == 3);
1 ✔
138
  CHECK(f.slot().levels.peak[0] == Catch::Approx(0.4));
1 ✔
139
  CHECK(f.slot().levels.peak[2] == Catch::Approx(0.3));
1 ✔
140
}
1 ✔
141

142
TEST_CASE("A channel that appears mid-way reads its own level", "[telemetry]")
1 ✔
143
{
144
  Fixture f;
1 ✔
145
  f.tick(constant({0.5}));
1 ✔
146
  f.tick(constant({0.5, 0.5}));
1 ✔
147
  REQUIRE(f.a.consume());
1 ✔
148
  CHECK(f.slot().levels.rms(0) == Catch::Approx(0.5));
1 ✔
149
  CHECK(f.slot().levels.rms(1) == Catch::Approx(0.5));
1 ✔
150
}
1 ✔
151

152
TEST_CASE("A tap that did not run reads as not running", "[telemetry]")
1 ✔
153
{
154
  Fixture f;
1 ✔
155
  f.a.tick(frames, 48000);
1 ✔
156
  REQUIRE(f.a.consume());
1 ✔
157
  CHECK(f.slot().levels.ticks == 0);
1 ✔
158

159
  f.tick({});
1 ✔
160
  REQUIRE(f.a.consume());
1 ✔
161
  CHECK(f.slot().levels.ticks == 1);
1 ✔
162
  CHECK(f.slot().levels.channels == 0);
1 ✔
163
}
1 ✔
164

165
TEST_CASE("Detaching hands the tap back", "[telemetry]")
1 ✔
166
{
167
  Fixture f;
1 ✔
168
  std::shared_ptr<meter_tap> none;
1 ✔
169
  f.a.attach(1, 8, tap_kind::none, none);
1 ✔
170
  REQUIRE(none);
1 ✔
171
  none.reset();
1 ✔
172
  CHECK(f.weak.expired());
1 ✔
173

174
  f.a.tick(frames, 48000);
1 ✔
175
  REQUIRE(f.a.consume());
1 ✔
176
  CHECK(f.slot().generation == 8);
1 ✔
177
  CHECK(f.slot().levels.ticks == 0);
1 ✔
178
}
1 ✔
179

180
TEST_CASE("Hardware taps read the driver's buffers", "[telemetry]")
1 ✔
181
{
182
  arena a{2};
1 ✔
183
  a.set_publish_interval(frames);
1 ✔
184
  auto in = std::make_shared<meter_tap>();
1 ✔
185
  auto out = std::make_shared<meter_tap>();
1 ✔
186
  a.attach(0, 1, tap_kind::hardware_inputs, in);
1 ✔
187
  a.attach(1, 1, tap_kind::hardware_outputs, out);
1 ✔
188

189
  std::array<float, frames> l{}, r{};
1 ✔
190
  l.fill(0.5f);
1 ✔
191
  r.fill(-0.75f);
1 ✔
192
  std::array<float*, 2> chans{l.data(), r.data()};
1 ✔
193
  ossia::audio_tick_state st{
1 ✔
194
      .inputs = chans.data(), .outputs = chans.data(), .n_in = 1, .n_out = 2,
2 ✔
195
      .frames = frames};
2 ✔
196
  a.accumulate_hardware(st);
1 ✔
197
  a.tick(frames, 48000);
1 ✔
198

199
  REQUIRE(a.consume());
1 ✔
200
  CHECK(a.latest().meters[0].levels.channels == 1);
1 ✔
201
  CHECK(a.latest().meters[1].levels.channels == 2);
1 ✔
202
  CHECK(a.latest().meters[1].levels.peak[1] == Catch::Approx(0.75));
1 ✔
203
  CHECK(a.latest().sample_rate == 48000);
1 ✔
204
  CHECK(a.latest().frames == frames);
1 ✔
205
}
1 ✔
206

207
TEST_CASE("An outlet meters what it carries after its gain", "[telemetry][dataflow]")
1 ✔
208
{
209
  ossia::audio_outlet out;
1 ✔
210
  auto tap = std::make_shared<meter_tap>();
1 ✔
211
  out.meter = tap;
1 ✔
212
  out.gain = 0.5;
1 ✔
213

214
  out->set_channels(1);
1 ✔
215
  out->channel(0).assign(frames, 1.);
1 ✔
216
  out.post_process();
1 ✔
217
  CHECK(tap->pending.peak[0] == Catch::Approx(0.5));
1 ✔
218

219
  out->set_channels(0);
1 ✔
220
  out.post_process();
1 ✔
221
  CHECK(tap->pending.ticks == 2);
1 ✔
222
}
1 ✔
223

224
TEST_CASE("A timed node's share of the real time comes back", "[telemetry][bench]")
1 ✔
225
{
226
  arena a{0, 2};
1 ✔
227
  a.set_publish_interval(frames);
1 ✔
228
  auto tap = std::make_shared<bench_tap>();
1 ✔
229
  std::weak_ptr<bench_tap> weak = tap;
1 ✔
230
  a.attach_bench(1, 3, tap);
1 ✔
231
  REQUIRE(!tap);
1 ✔
232

233
  // Half of a buffer of `frames` frames at 48 kHz, in two runs.
234
  const int64_t buffer_ns = int64_t(1e9 * frames / 48000);
1 ✔
235
  weak.lock()->pending.add(buffer_ns / 4);
1 ✔
236
  weak.lock()->pending.add(buffer_ns / 4);
1 ✔
237
  a.tick(frames, 48000);
1 ✔
238

239
  REQUIRE(a.consume());
1 ✔
240
  const auto& f = a.latest();
1 ✔
241
  const auto& s = f.benches[1];
1 ✔
242
  CHECK(s.generation == 3);
1 ✔
243
  CHECK(s.levels.runs == 2);
1 ✔
244
  CHECK(s.levels.max_ns == uint64_t(buffer_ns / 4));
1 ✔
245
  CHECK(f.window_frames == frames);
1 ✔
246
  CHECK(f.load(s) == Catch::Approx(0.5).epsilon(0.01));
1 ✔
247
}
1 ✔
248

249
TEST_CASE("Time not read yet is folded into the next frame", "[telemetry][bench]")
1 ✔
250
{
251
  arena a{0, 1};
1 ✔
252
  a.set_publish_interval(frames);
1 ✔
253
  auto tap = std::make_shared<bench_tap>();
1 ✔
254
  auto* t = tap.get();
1 ✔
255
  a.attach_bench(0, 1, tap);
1 ✔
256

257
  t->pending.add(100);
1 ✔
258
  a.tick(frames, 48000);
1 ✔
259
  t->pending.add(300);
1 ✔
260
  a.tick(frames, 48000);
1 ✔
261

262
  REQUIRE(a.consume());
1 ✔
263
  CHECK(a.latest().benches[0].levels.ns == 400);
1 ✔
264
  CHECK(a.latest().benches[0].levels.max_ns == 300);
1 ✔
265
  CHECK(a.latest().window_frames == 2 * frames);
1 ✔
266
}
1 ✔
267

268
TEST_CASE("A playhead publishes its latest position", "[telemetry][playhead]")
1 ✔
269
{
270
  arena a{0, 0, 2};
1 ✔
271
  a.set_publish_interval(frames);
1 ✔
272
  auto tap = std::make_shared<playhead_tap>();
1 ✔
273
  auto* t = tap.get();
1 ✔
274
  a.attach_playhead(1, 4, tap);
1 ✔
275

276
  t->running = true;
1 ✔
277
  t->date = 100;
1 ✔
278
  t->date = 250;
1 ✔
279
  a.tick(frames, 48000);
1 ✔
280

281
  REQUIRE(a.consume());
1 ✔
282
  const auto& p = a.latest().playheads[1];
1 ✔
283
  CHECK(p.generation == 4);
1 ✔
284
  CHECK(p.running);
1 ✔
285
  CHECK(p.date == 250);
1 ✔
286
  CHECK(!a.latest().playheads[0].running);
1 ✔
287
}
1 ✔
288

289
TEST_CASE("A virtual port meters what the graph writes to it", "[telemetry][audio]")
1 ✔
290
{
291
  ossia::audio_protocol* proto{};
1 ✔
292
  ossia::net::generic_device dev{
NEW
293
      [&] {
×
294
    auto p = std::make_unique<ossia::audio_protocol>();
1 ✔
295
    proto = p.get();
1 ✔
296
    return p;
1 ✔
297
  }(),
2 ✔
298
      "audio"};
3 ✔
299
  auto& node = ossia::net::create_node(dev.get_root_node(), "/bus");
1 ✔
300
  node.set_parameter(std::make_unique<ossia::virtual_audio_parameter>(2, node));
1 ✔
301
  auto& param = static_cast<ossia::virtual_audio_parameter&>(*node.get_parameter());
1 ✔
302
  proto->setup_buffers(
1 ✔
303
      {.inputs = nullptr, .outputs = nullptr, .n_in = 0, .n_out = 0, .frames = frames});
304

305
  ossia::audio_port port;
1 ✔
306
  port.set_channels(2);
1 ✔
307
  port.channel(0).assign(frames, 0.5);
1 ✔
308
  port.channel(1).assign(frames, -0.75);
1 ✔
309

310
  // Nothing is metered without a tap.
311
  param.push_value(port);
1 ✔
312

313
  meter_tap tap;
1 ✔
314
  param.meter = &tap;
1 ✔
315
  param.push_value(port);
1 ✔
316
  CHECK(tap.pending.ticks == 1);
1 ✔
317
  CHECK(tap.pending.channels == 2);
1 ✔
318
  CHECK(tap.pending.peak[0] == Catch::Approx(0.5));
1 ✔
319
  CHECK(tap.pending.peak[1] == Catch::Approx(0.75));
1 ✔
320

321
  param.meter = nullptr;
1 ✔
322
  param.push_value(port);
1 ✔
323
  CHECK(tap.pending.ticks == 1);
1 ✔
324
}
1 ✔
325

326
TEST_CASE("A parameter not written to during a tick is not touched", "[telemetry][audio]")
1 ✔
327
{
328
  // The execution keeps an entry per parameter it ever wrote to; a parameter
329
  // may be destroyed since, so only the ones written this tick are pushed.
330
  ossia::audio_protocol* proto{};
1 ✔
331
  ossia::net::generic_device dev{
NEW
332
      [&] {
×
333
    auto p = std::make_unique<ossia::audio_protocol>();
1 ✔
334
    proto = p.get();
1 ✔
335
    return p;
1 ✔
336
  }(),
2 ✔
337
      "audio"};
3 ✔
338
  auto& node = ossia::net::create_node(dev.get_root_node(), "/bus");
1 ✔
339
  node.set_parameter(std::make_unique<ossia::virtual_audio_parameter>(1, node));
1 ✔
340
  auto& param = static_cast<ossia::virtual_audio_parameter&>(*node.get_parameter());
1 ✔
341
  proto->setup_buffers(
1 ✔
342
      {.inputs = nullptr, .outputs = nullptr, .n_in = 0, .n_out = 0, .frames = frames});
343

344
  meter_tap tap;
1 ✔
345
  param.meter = &tap;
1 ✔
346

347
  ossia::execution_state st;
1 ✔
348
  ossia::audio_port port;
1 ✔
349
  port.set_channels(1);
1 ✔
350
  port.channel(0).assign(frames, 0.5);
1 ✔
351
  st.insert(param, port);
1 ✔
352
  st.commit();
1 ✔
353
  CHECK(tap.pending.ticks == 1);
1 ✔
354

355
  st.commit();
1 ✔
356
  CHECK(tap.pending.ticks == 1);
1 ✔
357

358
  st.insert(param, port);
1 ✔
359
  st.commit();
1 ✔
360
  CHECK(tap.pending.ticks == 2);
1 ✔
361
  param.meter = nullptr;
1 ✔
362
}
1 ✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc