45 const double bpm = std::max(1., tempo);
46 const double frames_per_quarter = rate * 60. / bpm;
47 auto to_tick = [&](int64_t frames) {
48 return int64_t(std::llround(frames / frames_per_quarter * ticks_per_quarter));
50 const int64_t end = std::max<int64_t>(1, to_tick(length));
58 std::vector<event> events;
59 events.reserve(std::size_t(note_count) * 4 + std::size_t(control_count) + 4);
62 const int us_per_quarter
63 = int(std::clamp<int64_t>(std::llround(60'000'000. / bpm), 1, 0xFFFFFF));
64 events.push_back({0, 1, libremidi::meta_events::tempo(us_per_quarter)});
68 {0, 1, libremidi::meta_events::time_signature(
69 std::clamp(meter_numerator, 1, 255),
70 std::clamp(meter_denominator, 1, 128), 24, 8)});
72 for(
int i = 0; i < note_count; i++)
74 const auto& n = notes[i];
75 const int64_t on = std::clamp<int64_t>(to_tick(n.start), 0, end - 1);
76 const int64_t len = std::max<int64_t>(1, to_tick(std::max<int64_t>(1, n.length)));
77 int64_t off = on + std::min(len, end - 1);
78 const int ch = std::clamp<int>(n.channel, 1, 16);
80 {on, 2, libremidi::channel_events::note_on(ch, n.pitch, std::max<int>(1, n.velocity))});
83 events.push_back({off, 0, libremidi::channel_events::note_off(ch, n.pitch, 0)});
89 events.push_back({end, 0, libremidi::channel_events::note_off(ch, n.pitch, 0)});
91 {0, 2, libremidi::channel_events::note_on(ch, n.pitch, std::max<int>(1, n.velocity))});
93 {std::max<int64_t>(1, off - end), 0, libremidi::channel_events::note_off(ch, n.pitch, 0)});
97 events.push_back({end, 0, libremidi::channel_events::note_off(ch, n.pitch, 0)});
101 for(
int i = 0; i < control_count; i++)
103 const auto& c = controls[i];
104 const int64_t t = std::clamp<int64_t>(to_tick(c.pos), 0, end - 1);
105 if((c.status & 0xF0) == 0xD0)
106 events.push_back({t, 1, libremidi::message{c.status, c.data1}});
108 events.push_back({t, 1, libremidi::message{c.status, c.data1, c.data2}});
113 events.push_back({end, 3, libremidi::message{0xFF, 0x06, 3,
'e',
'n',
'd'}});
115 std::stable_sort(events.begin(), events.end(), [](
const event& a,
const event& b) {
116 return a.tick != b.tick ? a.tick < b.tick : a.order < b.order;
120 w.ticksPerQuarterNote = ticks_per_quarter;
122 for(
const auto& e : events)
124 w.add_event(
int(e.tick - prev), 0, e.m);
128 std::ostringstream out;
130 return std::move(out).str();