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

ossia / libossia / 26645848395

29 May 2026 03:22PM UTC coverage: 46.949%. First build
26645848395

Pull #882

github

web-flow
Merge 52cd19ccc into 7d8ec2d14
Pull Request #882: ci: fix master CI failures across all workflows

12 of 25 new or added lines in 4 files covered. (48.0%)

25168 of 53607 relevant lines covered (46.95%)

141440.64 hits per line

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

0.0
/src/ossia/protocols/midi/midi_protocol.cpp
1
// This is an open source non-commercial project. Dear PVS-Studio, please check
2
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
3
#include <ossia/detail/logger.hpp>
4
#include <ossia/protocols/midi/detail/midi_impl.hpp>
5
#include <ossia/protocols/midi/midi_device.hpp>
6
#include <ossia/protocols/midi/midi_protocol.hpp>
7

8
#include <libremidi/backends/alsa_seq/config.hpp>
9
#include <libremidi/libremidi.hpp>
10
#include <libremidi/message.hpp>
11

12
namespace ossia::net::midi
13
{
14
static constexpr auto midi_api(libremidi::API api)
×
15
{
16
  return (api == libremidi::API::UNSPECIFIED) ? libremidi::midi1::default_api() : api;
×
17
}
18
midi_protocol::midi_protocol(
×
19
    ossia::net::network_context_ptr ctx, midi_protocol_configuration config,
20
    libremidi::input_configuration& conf, libremidi::input_api_configuration api)
×
21
    : protocol_base{flags{}}
22
    , m_context{ctx}
×
23
    , m_config{std::move(config)}
×
24
{
25
  conf.on_message = [this](libremidi::message&& m) { midi_callback(std::move(m)); };
×
26

27
  conf.ignore_timing = false;
×
28
  m_input = std::make_unique<libremidi::midi_in>(conf, api);
×
29
}
×
30

31
midi_protocol::midi_protocol(
×
32
    ossia::net::network_context_ptr ctx, midi_protocol_configuration config,
33
    libremidi::output_configuration& conf, libremidi::output_api_configuration api)
×
34
    : protocol_base{flags{}}
35
    , m_context{ctx}
×
36
    , m_config{std::move(config)}
×
37
{
38
  m_output = std::make_unique<libremidi::midi_out>(conf, api);
×
39
}
×
40

NEW
41
midi_protocol::midi_protocol(ossia::net::network_context_ptr ctx, midi_info info)
×
42
    : protocol_base{flags{}}
NEW
43
    , m_context{ctx}
×
44
{
NEW
45
  if(info.type == midi_info::Type::Input)
×
46
  {
NEW
47
    libremidi::input_configuration conf;
×
48
    conf.on_message
NEW
49
        = [this](libremidi::message&& m) { midi_callback(std::move(m)); };
×
NEW
50
    conf.ignore_timing = false;
×
NEW
51
    m_input = std::make_unique<libremidi::midi_in>(conf, libremidi::API::UNSPECIFIED);
×
NEW
52
  }
×
53
  else
54
  {
NEW
55
    libremidi::output_configuration conf;
×
NEW
56
    m_output = std::make_unique<libremidi::midi_out>(conf, libremidi::API::UNSPECIFIED);
×
NEW
57
  }
×
NEW
58
  set_info(std::move(info));
×
NEW
59
}
×
60

61
midi_protocol::~midi_protocol()
×
62
{
63
  if(m_input)
×
64
  {
65
    try
66
    {
67
      m_input->close_port();
×
68
    }
69
    catch(...)
×
70
    {
71
      logger().error("midi_protocol::~midi_protocol() error");
×
72
    }
×
73
  }
74

75
  if(m_output)
×
76
  {
77
    try
78
    {
79
      m_output->close_port();
×
80
    }
81
    catch(...)
×
82
    {
83
      logger().error("midi_protocol::~midi_protocol() error");
×
84
    }
×
85
  }
86
}
×
87

88
std::string
89
midi_protocol::get_midi_port_name(ossia::net::device_base* dev, const midi_info& info)
×
90
{
91
  std::string name{};
×
92
  const bool input = info.type == midi_info::Type::Input;
×
93
  if(dev)
×
94
    name = (input ? "i " : "o ") + dev->get_name();
×
95
  else
96
  {
97
    if(info.is_virtual)
×
98
      name = input ? "i libossia virtual" : "o libossia virtual";
×
99
    else
100
      name = input ? "i libossia" : "o libossia";
×
101
  }
102
  return name;
×
103
}
×
104

105
bool midi_protocol::set_info(midi_info m)
×
106
{
107
  try
108
  {
109
    // Close current ports
110
    if(m_info.type == midi_info::Type::Input)
×
111
    {
112
      if(m_input)
×
113
        m_input->close_port();
×
114
    }
115
    else if(m_info.type == midi_info::Type::Output)
×
116
    {
117
      if(m_output)
×
118
        m_output->close_port();
×
119
    }
120

121
    m_info = std::move(m);
×
122
    if(m_info.type == midi_info::Type::Input)
×
123
    {
124
      if(m_info.is_virtual)
×
125
        m_input->open_virtual_port(get_midi_port_name(m_dev, m_info));
×
126
      else
127
        m_input->open_port(
×
128
            static_cast<libremidi::input_port&>(m_info.handle),
×
129
            get_midi_port_name(m_dev, m_info));
×
130
    }
131
    else if(m_info.type == midi_info::Type::Output)
×
132
    {
133
      if(m_info.is_virtual)
×
134
        m_output->open_virtual_port(get_midi_port_name(m_dev, m_info));
×
135
      else
136
        m_output->open_port(
×
137
            static_cast<libremidi::output_port&>(m_info.handle),
×
138
            get_midi_port_name(m_dev, m_info));
×
139
    }
140

141
    return true;
×
142
  }
143
  catch(...)
×
144
  {
145
    logger().error("midi_protocol::~setInfo() error");
×
146
    return false;
×
147
  }
×
148
}
149

150
midi_info midi_protocol::get_info() const
×
151
{
152
  return m_info;
×
153
}
154

155
int64_t midi_protocol::get_timestamp() const noexcept
×
156
{
157
  if(m_info.type != midi_info::Type::Input || !m_input)
×
158
    return 0;
×
159
  return m_input->absolute_timestamp();
×
160
}
161

162
bool midi_protocol::pull(parameter_base& address)
×
163
{
164
  midi_parameter& adrs = dynamic_cast<midi_parameter&>(address);
×
165
  if(m_info.type != midi_info::Type::Input)
×
166
    return false;
×
167

168
  const address_info& adrinfo = adrs.info();
×
169
  switch(adrinfo.type)
×
170
  {
171
    case address_info::Type::NoteOn_N: {
×
172
      const midi_channel& chan = m_channels[adrinfo.channel - 1];
×
173
      int32_t val{chan.note_on_N[adrinfo.note]};
×
174
      address.set_value(val);
×
175
      return true;
×
176
    }
177

178
    case address_info::Type::NoteOn: {
×
179
      const midi_channel& chan = m_channels[adrinfo.channel - 1];
×
180
      std::vector<ossia::value> val{
181
          int32_t{chan.note_on.first}, int32_t{chan.note_on.second}};
×
182
      address.set_value(ossia::value{std::move(val)});
×
183
      return true;
×
184
    }
×
185

186
    case address_info::Type::NoteOff_N: {
×
187
      const midi_channel& chan = m_channels[adrinfo.channel - 1];
×
188
      if(m_config.velocity_zero_is_note_off)
×
189
      {
190
        int32_t val{chan.note_on_N[adrinfo.note]};
×
191
        address.set_value(0);
×
192
      }
193
      else
194
      {
195
        int32_t val{chan.note_off_N[adrinfo.note]};
×
196
        address.set_value(val);
×
197
      }
198
      return true;
×
199
    }
200

201
    case address_info::Type::NoteOff: {
×
202
      const midi_channel& chan = m_channels[adrinfo.channel - 1];
×
203

204
      if(m_config.velocity_zero_is_note_off)
×
205
      {
206
        std::vector<ossia::value> val{
207
                                      int32_t{chan.note_on.first}, int32_t{0}};
×
208
        address.set_value(ossia::value{std::move(val)});
×
209
      }
×
210
      else
211
      {
212
        std::vector<ossia::value> val{
213
                                      int32_t{chan.note_off.first}, int32_t{chan.note_off.second}};
×
214
        address.set_value(ossia::value{std::move(val)});
×
215
      }
×
216
      return true;
×
217
    }
218

219
    case address_info::Type::CC_N: {
×
220
      const midi_channel& chan = m_channels[adrinfo.channel - 1];
×
221
      int32_t val{chan.cc_N[adrinfo.note]};
×
222
      address.set_value(val);
×
223
      return true;
×
224
    }
225

226
    case address_info::Type::CC: {
×
227
      const midi_channel& chan = m_channels[adrinfo.channel - 1];
×
228
      std::vector<ossia::value> val{int32_t{chan.cc.first}, int32_t{chan.cc.second}};
×
229
      address.set_value(ossia::value{std::move(val)});
×
230
      return true;
×
231
    }
×
232

233
    case address_info::Type::PC: {
×
234
      const midi_channel& chan = m_channels[adrinfo.channel - 1];
×
235
      int32_t val{chan.pc};
×
236
      address.set_value(val);
×
237
      return true;
×
238
    }
239

240
    case address_info::Type::PB: {
×
241
      const midi_channel& chan = m_channels[adrinfo.channel - 1];
×
242
      int32_t val{chan.pb};
×
243
      address.set_value(val);
×
244
      return true;
×
245
    }
246
    default:
×
247
      return false;
×
248
  }
249
}
×
250

251
bool midi_protocol::push(const parameter_base& address, const ossia::value& v)
×
252
{
253
  try
254
  {
255
    const midi_parameter& adrs = dynamic_cast<const midi_parameter&>(address);
×
256
    if(m_info.type != midi_info::Type::Output)
×
257
      return false;
×
258
    static constexpr auto to_int = [](auto& v) { return ossia::convert<int32_t>(v); };
×
259

260
    auto& adrinfo = adrs.info();
×
261
    switch(adrinfo.type)
×
262
    {
263
      case address_info::Type::NoteOn_N: {
×
264
        m_output->send_message(libremidi::channel_events::note_on(
×
265
            adrinfo.channel, adrinfo.note, to_int(v)));
×
266
        return true;
×
267
      }
268

269
      case address_info::Type::NoteOn: {
×
270
        auto& val = v.get<std::vector<ossia::value>>();
×
271
        m_output->send_message(libremidi::channel_events::note_on(
×
272
            adrinfo.channel, to_int(val[0]), to_int(val[1])));
×
273
        return true;
×
274
      }
275

276
      case address_info::Type::NoteOff_N: {
×
277
        if(m_config.velocity_zero_is_note_off)
×
278
          m_output->send_message(libremidi::channel_events::note_on(
×
279
              adrinfo.channel, adrinfo.note, 0));
×
280
        else
281
          m_output->send_message(libremidi::channel_events::note_off(
×
282
              adrinfo.channel, adrinfo.note, to_int(v)));
×
283
        return true;
×
284
      }
285

286
      case address_info::Type::NoteOff: {
×
287
        auto& val = v.get<std::vector<ossia::value>>();
×
288
        if(m_config.velocity_zero_is_note_off)
×
289
          m_output->send_message(libremidi::channel_events::note_on(
×
290
              adrinfo.channel, to_int(val[0]), 0));
×
291
        else
292
          m_output->send_message(libremidi::channel_events::note_off(
×
293
              adrinfo.channel, to_int(val[0]), to_int(val[1])));
×
294
        return true;
×
295
      }
296

297
      case address_info::Type::CC_N: {
×
298
        m_output->send_message(libremidi::channel_events::control_change(
×
299
            adrinfo.channel, adrinfo.note, to_int(v)));
×
300
        return true;
×
301
      }
302

303
      case address_info::Type::CC: {
×
304
        auto& val = v.get<std::vector<ossia::value>>();
×
305
        m_output->send_message(libremidi::channel_events::control_change(
×
306
            adrinfo.channel, to_int(val[0]), to_int(val[1])));
×
307
        return true;
×
308
      }
309

310
      case address_info::Type::PC: {
×
311
        m_output->send_message(
×
312
            libremidi::channel_events::program_change(adrinfo.channel, to_int(v)));
×
313
        return true;
×
314
      }
315

316
      case address_info::Type::PC_N: {
×
317
        m_output->send_message(
×
318
            libremidi::channel_events::program_change(adrinfo.channel, adrinfo.note));
×
319
        return true;
×
320
      }
321

322
      case address_info::Type::PB: {
×
323
        m_output->send_message(
×
324
            libremidi::channel_events::pitch_bend(adrinfo.channel, to_int(v)));
×
325
        return true;
×
326
      }
327

328
      case address_info::Type::Any: {
×
329
        if(auto vec = v.target<std::vector<ossia::value>>())
×
330
        {
331
          libremidi::message m;
×
332
          m.bytes.reserve(vec->size());
×
333
          for(const auto& val : *vec)
×
334
          {
335
            m.bytes.push_back(ossia::convert<int32_t>(val));
×
336
          }
337
          m_output->send_message(std::move(m));
×
338
        }
×
339
        return true;
×
340
      }
341
      default:
×
342
        return false;
×
343
    }
344

345
    return true;
346
  }
347
  catch(const std::exception& e)
×
348
  {
349
    ossia::logger().error("Error when pushing midi message: {}", e.what());
×
350
    return false;
×
351
  }
×
352
  catch(...)
×
353
  {
354
    ossia::logger().error("Error when pushing midi message");
×
355
    return false; // TODO log error.
×
356
  }
×
357
}
358

359
bool midi_protocol::push_raw(const full_parameter_data& parameter_base)
×
360
{
361
  return false;
×
362
}
363

364
bool midi_protocol::observe(parameter_base& address, bool enable)
×
365
{
366
  if(m_info.type != midi_info::Type::Input)
×
367
    return false;
×
368

369
  midi_parameter& adrs = dynamic_cast<midi_parameter&>(address);
×
370
  auto& adrinfo = adrs.info();
×
371
  switch(adrinfo.type)
×
372
  {
373
    case address_info::Type::NoteOn: {
×
374
      midi_channel& chan = m_channels[adrinfo.channel - 1];
×
375
      chan.callback_note_on = enable;
×
376
      return true;
×
377
    }
378
    case address_info::Type::NoteOff: {
×
379
      midi_channel& chan = m_channels[adrinfo.channel - 1];
×
380
      if(m_config.velocity_zero_is_note_off)
×
381
        chan.callback_note_on = enable;
×
382
      else
383
        chan.callback_note_off = enable;
×
384
      return true;
×
385
    }
386
    case address_info::Type::CC: {
×
387
      midi_channel& chan = m_channels[adrinfo.channel - 1];
×
388
      chan.callback_cc = enable;
×
389
      return true;
×
390
    }
391
    case address_info::Type::PC: {
×
392
      midi_channel& chan = m_channels[adrinfo.channel - 1];
×
393
      chan.callback_pc = enable;
×
394
      return true;
×
395
    }
396
    case address_info::Type::NoteOn_N: {
×
397
      midi_channel& chan = m_channels[adrinfo.channel - 1];
×
398
      chan.callback_note_on_N[adrinfo.note] = enable;
×
399
      return true;
×
400
    }
401
    case address_info::Type::NoteOff_N: {
×
402
      midi_channel& chan = m_channels[adrinfo.channel - 1];
×
403
      if(m_config.velocity_zero_is_note_off)
×
404
        chan.callback_note_on_N[adrinfo.note] = enable;
×
405
      else
406
        chan.callback_note_off_N[adrinfo.note] = enable;
×
407
      return true;
×
408
    }
409
    case address_info::Type::CC_N: {
×
410
      midi_channel& chan = m_channels[adrinfo.channel - 1];
×
411
      chan.callback_cc_N[adrinfo.note] = enable;
×
412
      return true;
×
413
    }
414
    case address_info::Type::PC_N: {
×
415
      midi_channel& chan = m_channels[adrinfo.channel - 1];
×
416
      chan.callback_pc_N[adrinfo.note] = enable;
×
417
      return true;
×
418
    }
419
    case address_info::Type::PB: {
×
420
      midi_channel& chan = m_channels[adrinfo.channel - 1];
×
421
      chan.callback_pb = enable;
×
422
      return true;
×
423
    }
424

425
    default:
×
426
      // TODO do the non-N version
427
      return false;
×
428
  }
429

430
  return true;
431
}
432

433
bool midi_protocol::update(node_base& node)
×
434
{
435
  return false;
×
436
}
437

438
void midi_protocol::register_node_base(const ossia::net::node_base& n)
×
439
{
440
  ossia::net::iterate_all_children(
×
441
      (ossia::net::node_base*)&n, [this](ossia::net::parameter_base& adrs) {
×
442
        if(auto p = dynamic_cast<midi_parameter*>(&adrs))
×
443
          register_parameter(*p);
×
444
      });
×
445
}
×
446
void midi_protocol::unregister_node_base(const ossia::net::node_base& n)
×
447
{
448
  ossia::net::iterate_all_children(
×
449
      (ossia::net::node_base*)&n, [this](ossia::net::parameter_base& adrs) {
×
450
        if(auto p = dynamic_cast<midi_parameter*>(&adrs))
×
451
          unregister_parameter(*p);
×
452
      });
×
453
}
×
454

455
void midi_protocol::register_parameter_base(const ossia::net::parameter_base& adrs)
×
456
{
457
  if(auto p = dynamic_cast<const midi_parameter*>(&adrs))
×
458
    register_parameter(const_cast<midi_parameter&>(*p));
×
459
}
×
460
void midi_protocol::unregister_parameter_base(const ossia::net::parameter_base& adrs)
×
461
{
462
  if(auto p = dynamic_cast<const midi_parameter*>(&adrs))
×
463
    unregister_parameter(const_cast<midi_parameter&>(*p));
×
464
}
×
465

466
void midi_protocol::register_parameter(midi_parameter& adrs)
×
467
{
468
  auto adrs_ptr = &adrs;
×
469
  auto& adrinfo = adrs.info();
×
470
  switch(adrinfo.type)
×
471
  {
472
    case address_info::Type::NoteOn: {
×
473
      midi_channel& chan = m_channels[adrinfo.channel - 1];
×
474
      chan.param_note_on = adrs_ptr;
×
475
      break;
×
476
    }
477
    case address_info::Type::NoteOff: {
×
478
      midi_channel& chan = m_channels[adrinfo.channel - 1];
×
479
      chan.param_note_off = adrs_ptr;
×
480
      break;
×
481
    }
482
    case address_info::Type::CC: {
×
483
      midi_channel& chan = m_channels[adrinfo.channel - 1];
×
484
      chan.param_cc = adrs_ptr;
×
485
      break;
×
486
    }
487
    case address_info::Type::PC: {
×
488
      midi_channel& chan = m_channels[adrinfo.channel - 1];
×
489
      chan.param_pc = adrs_ptr;
×
490
      break;
×
491
    }
492
    case address_info::Type::NoteOn_N: {
×
493
      midi_channel& chan = m_channels[adrinfo.channel - 1];
×
494
      chan.param_note_on_N[adrinfo.note] = adrs_ptr;
×
495
      break;
×
496
    }
497
    case address_info::Type::NoteOff_N: {
×
498
      midi_channel& chan = m_channels[adrinfo.channel - 1];
×
499
      chan.param_note_off_N[adrinfo.note] = adrs_ptr;
×
500
      break;
×
501
    }
502
    case address_info::Type::CC_N: {
×
503
      midi_channel& chan = m_channels[adrinfo.channel - 1];
×
504
      chan.param_cc_N[adrinfo.note] = adrs_ptr;
×
505
      break;
×
506
    }
507
    case address_info::Type::PC_N: {
×
508
      midi_channel& chan = m_channels[adrinfo.channel - 1];
×
509
      chan.param_pc_N[adrinfo.note] = adrs_ptr;
×
510
      break;
×
511
    }
512
    case address_info::Type::PB: {
×
513
      midi_channel& chan = m_channels[adrinfo.channel - 1];
×
514
      chan.param_pb = adrs_ptr;
×
515
      break;
×
516
    }
517
    default:
×
518
      break;
×
519
  }
520
}
×
521

522
void midi_protocol::unregister_parameter(midi_parameter& adrs)
×
523
{
524
  auto& adrinfo = adrs.info();
×
525
  switch(adrinfo.type)
×
526
  {
527
    case address_info::Type::NoteOn: {
×
528
      midi_channel& chan = m_channels[adrinfo.channel - 1];
×
529
      chan.param_note_on = nullptr;
×
530
      break;
×
531
    }
532
    case address_info::Type::NoteOff: {
×
533
      midi_channel& chan = m_channels[adrinfo.channel - 1];
×
534
      chan.param_note_off = nullptr;
×
535
      break;
×
536
    }
537
    case address_info::Type::CC: {
×
538
      midi_channel& chan = m_channels[adrinfo.channel - 1];
×
539
      chan.param_cc = nullptr;
×
540
      break;
×
541
    }
542
    case address_info::Type::PC: {
×
543
      midi_channel& chan = m_channels[adrinfo.channel - 1];
×
544
      chan.param_pc = nullptr;
×
545
      break;
×
546
    }
547
    case address_info::Type::NoteOn_N: {
×
548
      midi_channel& chan = m_channels[adrinfo.channel - 1];
×
549
      chan.param_note_on_N[adrinfo.note] = nullptr;
×
550
      break;
×
551
    }
552
    case address_info::Type::NoteOff_N: {
×
553
      midi_channel& chan = m_channels[adrinfo.channel - 1];
×
554
      chan.param_note_off_N[adrinfo.note] = nullptr;
×
555
      break;
×
556
    }
557
    case address_info::Type::CC_N: {
×
558
      midi_channel& chan = m_channels[adrinfo.channel - 1];
×
559
      chan.param_cc_N[adrinfo.note] = nullptr;
×
560
      break;
×
561
    }
562
    case address_info::Type::PC_N: {
×
563
      midi_channel& chan = m_channels[adrinfo.channel - 1];
×
564
      chan.param_pc_N[adrinfo.note] = nullptr;
×
565
      break;
×
566
    }
567
    case address_info::Type::PB: {
×
568
      midi_channel& chan = m_channels[adrinfo.channel - 1];
×
569
      chan.param_pb = nullptr;
×
570
      break;
×
571
    }
572
    default:
×
573
      break;
×
574
  }
575
}
×
576

577
void midi_protocol::set_device(device_base& dev)
×
578
{
579
  // FIXME handle reassignment of device
580
  assert(!m_dev);
×
581
  m_dev = static_cast<midi_device*>(&dev);
×
582
  m_dev->on_node_created.connect<&midi_protocol::register_node_base>(this);
×
583
  m_dev->on_node_removing.connect<&midi_protocol::unregister_node_base>(this);
×
584
  m_dev->on_parameter_created.connect<&midi_protocol::register_parameter_base>(this);
×
585
  m_dev->on_parameter_removing.connect<&midi_protocol::unregister_parameter_base>(this);
×
586

587
  if(m_info.type == midi_info::Type::Input)
×
588
  {
589
    // Setup callbacks
590
    ossia::net::iterate_all_children(m_dev, [this](ossia::net::parameter_base& p) {
×
591
      register_parameter(dynamic_cast<midi_parameter&>(p));
×
592
    });
×
593
  }
594
}
×
595

596
void midi_protocol::value_callback(
×
597
    bool observed, parameter_base& param, const value& val)
598
{
599
  observed ? param.set_value(val) : param.set_value_quiet(val);
×
600

601
  m_dev->on_message(param);
×
602
}
×
603

604
void midi_protocol::midi_callback(libremidi::message&& mess)
×
605
{
606
  if(m_logger.inbound_logger)
×
607
  {
608
    if(mess.bytes.size() >= 3)
×
609
      m_logger.inbound_logger->info(
×
610
          "MIDI in: {0} {1} {2}", mess.bytes[0], mess.bytes[1], mess.bytes[2]);
×
611
    else if(mess.bytes.size() == 2)
×
612
      m_logger.inbound_logger->info("MIDI in: {0} {1}", mess.bytes[0], mess.bytes[1]);
×
613
    else if(mess.bytes.size() == 1)
×
614
      m_logger.inbound_logger->info("MIDI in: {0}", mess.bytes[0]);
×
615
  }
616

617
  if(this->m_config.velocity_zero_is_note_off) {
×
618
    if(mess.get_message_type() == libremidi::message_type::NOTE_ON) {
×
619
      const auto status = mess.bytes[0];
×
620
      const auto note = mess.bytes[1];
×
621
      const auto vel = mess.bytes[2];
×
622
      if(vel == 0)
×
623
        mess.bytes[0] = (status & 0x0F) | 0x80;
×
624
    }
625
  }
626

627
  if(m_learning)
×
628
  {
629
    on_learn(mess);
×
630
    return;
×
631
  }
632

633
  if(m_registers)
×
634
  {
635
    this->to_midi2.convert(
×
636
        mess.bytes.data(), mess.bytes.size(), mess.timestamp,
×
637
        [&](const uint32_t* ump, int count, auto ts) {
×
638
      libremidi::ump u;
×
639
      std::copy_n(ump, std::min(count, 4), u.data);
×
640
      u.timestamp = ts;
×
641

642
      messages.enqueue(u);
×
643

644
      return stdx::error{};
×
645
    });
646
  }
647

648
  const auto chan = mess.get_channel();
×
649
  if(chan == 0)
×
650
    return;
×
651

652
  midi_channel& c = m_channels[chan - 1];
×
653
  switch(mess.get_message_type())
×
654
  {
655
    case libremidi::message_type::NOTE_ON:
×
656
      c.note_on.first = mess.bytes[1];
×
657
      c.note_on.second = mess.bytes[2];
×
658
      c.note_on_N[c.note_on.first] = c.note_on.second;
×
659
      if(c.param_note_on)
×
660
      {
661
        std::vector<ossia::value> t{int32_t{c.note_on.first}, int32_t{c.note_on.second}};
×
662
        value_callback(c.callback_note_on, *c.param_note_on, ossia::value{std::move(t)});
×
663
      }
×
664
      if(c.param_note_on_N[c.note_on.first])
×
665
      {
666
        int32_t val{c.note_on_N[c.note_on.first]};
×
667
        value_callback(
×
668
            c.callback_note_on_N[c.note_on.first], *c.param_note_on_N[c.note_on.first],
×
669
            val);
670
      }
671
      break;
×
672
    case libremidi::message_type::NOTE_OFF:
×
673
      c.note_off.first = mess.bytes[1];
×
674
      c.note_off.second = mess.bytes[2];
×
675
      c.note_off_N[c.note_off.first] = c.note_off.second;
×
676
      if(c.param_note_off)
×
677
      {
678
        std::vector<ossia::value> t{
679
            int32_t{c.note_off.first}, int32_t{c.note_off.second}};
×
680
        value_callback(
×
681
            c.callback_note_off, *c.param_note_off, ossia::value{std::move(t)});
×
682
      }
×
683
      if(c.param_note_off_N[c.note_off.first])
×
684
      {
685
        int32_t val{c.note_off_N[c.note_off.first]};
×
686
        value_callback(
×
687
            c.callback_note_off_N[c.note_off.first],
×
688
            *c.param_note_off_N[c.note_off.first], val);
×
689
      }
690
      break;
×
691
    case libremidi::message_type::CONTROL_CHANGE:
×
692
      c.cc.first = mess.bytes[1];
×
693
      c.cc.second = mess.bytes[2];
×
694
      c.cc_N[c.cc.first] = c.cc.second;
×
695
      if(c.param_cc)
×
696
      {
697
        std::vector<ossia::value> t{int32_t{c.cc.first}, int32_t{c.cc.second}};
×
698
        value_callback(c.callback_cc, *c.param_cc, ossia::value{std::move(t)});
×
699
      }
×
700
      if(c.param_cc_N[c.cc.first])
×
701
      {
702
        int32_t val{c.cc_N[c.cc.first]};
×
703
        value_callback(c.callback_cc_N[c.cc.first], *c.param_cc_N[c.cc.first], val);
×
704
      }
705
      break;
×
706
    case libremidi::message_type::PROGRAM_CHANGE:
×
707
      c.pc = mess.bytes[1];
×
708
      if(c.param_pc)
×
709
      {
710
        value_callback(c.callback_pc, *c.param_pc, int32_t{c.pc});
×
711
      }
712
      if(c.param_pc_N[c.pc])
×
713
      {
714
        value_callback(c.callback_pc_N[c.pc], *c.param_pc_N[c.pc], ossia::impulse{});
×
715
      }
716
      break;
×
717
    case libremidi::message_type::PITCH_BEND:
×
718
      c.pb = mess.bytes[2] * 128 + mess.bytes[1];
×
719
      if(c.param_pb)
×
720
      {
721
        value_callback(c.callback_pb, *c.param_pb, int32_t{c.pb});
×
722
      }
723
      break;
×
724
    default:
×
725
      break;
×
726
  }
727
}
×
728

729
template <typename T, typename... Args>
730
midi_node* find_or_create(
×
731
    std::string_view name, midi_device& dev, midi_node& parent, Args&&... args)
732
{
733
  auto n = parent.find_child(name);
×
734
  if(!n)
×
735
    return parent.add_midi_node(
×
736
        std::make_unique<T>(std::forward<Args>(args)..., dev, parent));
×
737

738
  return dynamic_cast<midi_node*>(n);
×
739
}
740

741
void midi_protocol::on_learn(const libremidi::message& mess)
×
742
{
743
  const midi_size_t chan = mess.get_channel();
×
744
  if(chan == 0)
×
745
    return;
×
746

747
  auto channel_node = find_or_create<ossia::net::midi::channel_node>(
×
748
      midi_node_name(chan), *m_dev, *m_dev, false, chan);
×
749

750
  switch(mess.get_message_type())
×
751
  {
752
    case libremidi::message_type::NOTE_ON: {
×
753
      auto node = find_or_create<generic_node>(
×
754
          "on", *m_dev, *channel_node,
×
755
          address_info{chan, address_info::Type::NoteOn, 0});
×
756
      register_parameter(static_cast<midi_parameter&>(*node->get_parameter()));
×
757

758
      auto n = mess.bytes[1];
×
759
      auto cld = find_or_create<generic_node>(
×
760
          midi_node_name(n), *m_dev, *node,
×
761
          address_info{chan, address_info::Type::NoteOn_N, n});
×
762
      auto& param = static_cast<midi_parameter&>(*cld->get_parameter());
×
763
      register_parameter(param);
×
764
      param.set_value(mess.bytes[2]);
×
765
      break;
×
766
    }
767

768
    case libremidi::message_type::NOTE_OFF: {
×
769
      auto node = find_or_create<generic_node>(
×
770
          "off", *m_dev, *channel_node,
×
771
          address_info{chan, address_info::Type::NoteOff, 0});
×
772
      register_parameter(static_cast<midi_parameter&>(*node->get_parameter()));
×
773

774
      auto n = mess.bytes[1];
×
775
      auto cld = find_or_create<generic_node>(
×
776
          midi_node_name(n), *m_dev, *node,
×
777
          address_info{chan, address_info::Type::NoteOff_N, n});
×
778
      auto& param = static_cast<midi_parameter&>(*cld->get_parameter());
×
779
      register_parameter(param);
×
780
      param.set_value(mess.bytes[2]);
×
781
      break;
×
782
    }
783

784
    case libremidi::message_type::CONTROL_CHANGE: {
×
785
      auto node = find_or_create<generic_node>(
×
786
          "control", *m_dev, *channel_node,
×
787
          address_info{chan, address_info::Type::CC, 0});
×
788
      register_parameter(static_cast<midi_parameter&>(*node->get_parameter()));
×
789

790
      auto n = mess.bytes[1];
×
791
      auto cld = find_or_create<generic_node>(
×
792
          midi_node_name(n), *m_dev, *node,
×
793
          address_info{chan, address_info::Type::CC_N, n});
×
794
      auto& param = static_cast<midi_parameter&>(*cld->get_parameter());
×
795
      register_parameter(param);
×
796
      param.set_value(mess.bytes[2]);
×
797
      break;
×
798
    }
799

800
    case libremidi::message_type::PROGRAM_CHANGE: {
×
801
      auto node = find_or_create<generic_node>(
×
802
          "program", *m_dev, *channel_node,
×
803
          address_info{chan, address_info::Type::PC, 0});
×
804
      register_parameter(static_cast<midi_parameter&>(*node->get_parameter()));
×
805

806
      auto n = mess.bytes[1];
×
807
      auto cld = find_or_create<generic_node>(
×
808
          midi_node_name(n), *m_dev, *node,
×
809
          address_info{chan, address_info::Type::PC_N, n});
×
810
      register_parameter(static_cast<midi_parameter&>(*cld->get_parameter()));
×
811
      break;
×
812
    }
813

814
    case libremidi::message_type::PITCH_BEND: {
×
815
      auto node = find_or_create<generic_node>(
×
816
          "pitchbend", *m_dev, *channel_node,
×
817
          address_info{chan, address_info::Type::PB, 0});
×
818
      register_parameter(static_cast<midi_parameter&>(*node->get_parameter()));
×
819
      break;
×
820
    }
821
    default:
×
822
      break;
×
823
  }
824
}
825

826
std::vector<midi_info> midi_protocol::scan(libremidi::API api)
×
827
{
828
  api = midi_api(api);
×
829

830
  std::vector<midi_info> vec;
×
831

832
  libremidi::observer_configuration conf{};
×
833
  conf.track_hardware = true;
×
834
  conf.track_virtual = true;
×
835

836
  libremidi::observer in{conf, libremidi::observer_configuration_for(api)};
×
837

838
  for(auto& port : in.get_input_ports())
×
839
    vec.emplace_back(port, false);
×
840
  for(auto& port : in.get_output_ports())
×
841
    vec.emplace_back(port, false);
×
842

843
  return vec;
×
844
}
×
845

846
void midi_protocol::push_value(const libremidi::message& m)
×
847
{
848
  m_output->send_message(m);
×
849
}
×
850

851
void midi_protocol::push_value(const libremidi::ump& m)
×
852
{
853
  m_output->send_ump(m);
×
854
}
×
855

856
void midi_protocol::enable_registration()
×
857
{
858
  m_registers = true;
×
859
}
×
860

861
bool midi_protocol::learning() const
×
862
{
863
  return m_learning;
×
864
}
865

866
void midi_protocol::set_learning(bool newLearn)
×
867
{
868
  m_learning = newLearn;
×
869
}
×
870
}
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