2 #include <ossia/detail/string_map.hpp>
4 #include <halp/audio.hpp>
5 #include <halp/controls.hpp>
6 #include <halp/meta.hpp>
7 #include <halp/midi.hpp>
8 #include <libremidi/message.hpp>
16 halp_meta(name,
"Chord")
17 halp_meta(c_name,
"Chord")
18 halp_meta(category,
"Midi")
19 halp_meta(author,
"ossia score")
20 halp_meta(manual_url,
"https://ossia.io/score-docs/processes/midi-utilities.html#chord")
21 halp_meta(description,
"Generate a chord from a single note")
22 halp_meta(uuid,
"F0904279-EA26-48DB-B0DF-F68FE3091DA1");
33 using midi_out = halp::midi_out_bus<
"out", libremidi::message>;
36 halp::midi_bus<
"in", libremidi::message> midi;
37 halp::hslider_i32<
"Num. Notes", halp::irange{1, 5, 3}> num;
38 halp::enum_t<Chord,
"Chord"> chord;
46 std::string_view ch{};
49 ossia::flat_map<uint8_t, std::vector<chord_type>> chords;
53 static const constexpr std::array<int, 5> major7{0, 4, 7, 11, 12};
54 static const constexpr std::array<int, 5> minor7{0, 3, 7, 10, 12};
55 static const constexpr std::array<int, 5> sus2{0, 2, 7, 9, 12};
56 static const constexpr std::array<int, 5> sus4{0, 5, 7, 9, 12};
57 static const constexpr std::array<int, 5> dim{0, 3, 6, 9, 12};
58 static const constexpr std::array<int, 5> aug{0, 4, 8, 10, 12};
61 static void startChord(
const T& chord,
const libremidi::message& m,
const std::size_t num, midi_out& op)
63 for(std::size_t i = 0; i < std::min(num, chord.size()); i++)
65 auto new_note = m.bytes[1] + chord[i];
69 op.note_on(m.get_channel(), new_note, m.bytes[2]).timestamp = m.timestamp;
74 static void stopChord(
const T& chord,
const libremidi::message& m,
const std::size_t num, midi_out& op)
76 for(std::size_t i = 0; i < std::min(num, chord.size()); i++)
78 auto new_note = m.bytes[1] + chord[i];
82 op.note_off(m.get_channel(), new_note, m.bytes[2]).timestamp = m.timestamp;
87 static void dispatchChord(std::string_view chord,
const libremidi::message& m,
int num, midi_out& op, F&& f)
89 static const ossia::string_view_map<std::array<int, 5>> chords{{
"Maj", major7}, {
"Min", minor7}, {
"Sus2", sus2}, {
"Sus4", sus4}, {
"Dim", dim}, {
"Aug", aug}};
90 auto it = chords.find(chord);
91 if(it != chords.end())
92 f(it->second, m, num, op);
96 using tick = halp::tick_musical;
97 void operator()(
const halp::tick_musical& tk)
99 for(
const libremidi::message& m : inputs.midi)
101 int lastNum = inputs.num;
102 std::string_view lastCh = magic_enum::enum_name<Chord>(inputs.chord);
103 if(m.get_message_type() == libremidi::message_type::NOTE_ON)
105 auto cur = m.bytes[1];
106 this->chords[cur].push_back({lastCh, lastNum});
107 dispatchChord(lastCh, m, lastNum, outputs.midi, [](
auto&&... args) { startChord(args...); });
109 else if(m.get_message_type() == libremidi::message_type::NOTE_OFF)
111 auto it = this->chords.find(m.bytes[1]);
112 if(it != this->chords.end())
114 for(
const auto& chord : it->second)
116 dispatchChord(chord.ch, m, chord.notes, outputs.midi, [](
auto&&... args) { stopChord(args...); });
118 const_cast<std::vector<chord_type>&
>(it->second).clear();
124 outputs.midi.push_back(m);