2 #include <Engine/Node/SimpleApi.hpp>
3 #include <Fx/Quantifier.hpp>
5 #include <ossia/detail/math.hpp>
7 #include <rnd/random.hpp>
17 static const constexpr
auto prettyName =
"Pulse to Midi";
18 static const constexpr
auto objectKey =
"VelToNote";
19 static const constexpr
auto category =
"Midi";
20 static const constexpr
auto author =
"ossia score";
21 static const constexpr
auto kind = Process::ProcessCategory::Other;
22 static const constexpr
auto description
23 =
"Converts a message into MIDI.\n"
24 "If the input is an impulse, the output will be the default pitch "
25 "at the default velocity.\n"
26 "If the input is a single integer in [0; 127], the output will be "
27 "the relevant note at the default velocity"
28 "If the input is an array of two values between [0; 127], the "
29 "output will be the relevant note.";
31 static const constexpr
auto tags = std::array<const char*, 0>{};
32 static const uuid_constexpr
auto uuid
33 = make_uuid(
"2c6493c3-5449-4e52-ae04-9aee3be5fb6a");
35 static const constexpr value_in value_ins[]{{
"in",
true}};
36 static const constexpr midi_out midi_outs[]{
"out"};
37 static const constexpr
auto controls = tuplet::make_tuple(
39 "Start quant.", 2, Control::Widgets::notes),
42 "End quant.", 2, Control::Widgets::notes),
43 Control::Widgets::MidiSpinbox(
"Default pitch"),
44 Control::Widgets::MidiSpinbox(
"Default vel."),
45 Control::Widgets::OctaveSlider(
"Pitch shift", -5, 5),
46 Control::Widgets::OctaveSlider(
"Pitch random", 0, 2),
47 Control::Widgets::OctaveSlider(
"Vel. random", 0, 2),
48 Control::Widgets::MidiChannel(
"Channel"));
54 ossia::time_value date{};
58 std::vector<NoteIn> to_start;
59 std::vector<NoteIn> running_notes;
62 using control_policy = ossia::safe_nodes::default_tick_controls;
69 Note operator()() {
return {base_note, base_vel}; }
70 Note operator()(ossia::impulse) {
return {base_note, base_vel}; }
72 Note operator()(
const T&)
74 return {base_note, base_vel};
76 Note operator()(
float note) {
return {(uint8_t)note, base_vel}; }
77 Note operator()(
char note) {
return {(uint8_t)note, base_vel}; }
78 Note operator()(
int note) {
return {(uint8_t)note, base_vel}; }
79 Note operator()(
int note,
int vel) {
return {(uint8_t)note, (uint8_t)vel}; }
80 Note operator()(
int note,
float vel)
82 return {(uint8_t)note, (uint8_t)(vel * 127.f)};
84 Note operator()(
const std::vector<ossia::value>& v)
91 return operator()(ossia::convert<int>(v[0]));
93 int note = ossia::convert<int>(v[0]);
94 switch(v[1].get_type())
96 case ossia::val_type::FLOAT:
97 return operator()(note, *v[1].v.target<
float>());
98 case ossia::val_type::INT:
99 return operator()(note, *v[1].v.target<
int>());
101 return operator()(note, ossia::convert<int>(v[1]));
105 return operator()(ossia::convert<int>(v[0]), ossia::convert<int>(v[1]));
108 template <std::
size_t N>
109 Note operator()(
const std::array<float, N>& v)
111 static_assert(N >= 2);
112 return operator()(v[0], v[1]);
116 static constexpr uint8_t midi_clamp(
int num)
118 return (uint8_t)ossia::clamp(num, 0, 127);
121 run(
const ossia::value_port& p1,
const ossia::timed_vec<float>& startq,
122 const ossia::timed_vec<float>& tightness,
const ossia::timed_vec<float>& endq,
123 const ossia::timed_vec<int>& basenote,
const ossia::timed_vec<int>& basevel,
124 const ossia::timed_vec<int>& shift_note,
const ossia::timed_vec<int>& note_random,
125 const ossia::timed_vec<int>& vel_random,
const ossia::timed_vec<int>& chan_vec,
126 ossia::midi_port& p2, ossia::token_request tk, ossia::exec_state_facade st,
143 auto start = startq.rbegin()->second;
145 auto end = endq.rbegin()->second;
146 auto shiftnote = shift_note.rbegin()->second;
147 auto base_note = midi_clamp(basenote.rbegin()->second);
148 auto base_vel = midi_clamp(basevel.rbegin()->second);
149 auto rand_note = note_random.rbegin()->second;
150 auto rand_vel = vel_random.rbegin()->second;
151 auto chan = chan_vec.rbegin()->second;
153 const double sampleRatio = st.modelToSamples();
154 for(
auto& in : p1.get_data())
156 auto note = in.value.apply(val_visitor{
self, base_note, base_vel});
159 note.pitch += rnd::rand(-rand_note, rand_note);
161 note.vel += rnd::rand(-rand_vel, rand_vel);
163 note.pitch = ossia::clamp((
int)note.pitch + shiftnote, 0, 127);
164 note.vel = ossia::clamp((
int)note.vel, 0, 127);
170 auto& no = p2.note_on(chan, note.pitch, note.vel);
171 no.timestamp = in.timestamp;
174 self.running_notes.push_back(
175 {note, tk.from_physical_time_in_tick(in.timestamp, sampleRatio)});
180 p2.note_off(chan, note.pitch, note.vel).timestamp = no.timestamp;
187 self.to_start.push_back({note, ossia::time_value{}});
193 p2.note_off(chan, note.pitch, note.vel).timestamp = in.timestamp;
199 if(
auto date = tk.get_quantification_date(1. / start))
202 *date, tk.to_physical_time_in_tick(*date, sampleRatio), chan, end, p2,
self);
208 tk.prev_date, tk.to_physical_time_in_tick(tk.prev_date, sampleRatio), chan,
214 if(
auto date = tk.get_quantification_date(1. / end))
216 stop_notes(tk.to_physical_time_in_tick(*date, sampleRatio), chan, p2,
self);
221 stop_notes(tk.to_physical_time_in_tick(tk.prev_date, sampleRatio), chan, p2,
self);
225 static void start_all_notes(
226 ossia::time_value date, ossia::physical_time date_phys,
int chan,
float endq,
227 ossia::midi_port& p2,
State&
self) noexcept
229 for(
auto& note :
self.to_start)
231 p2.note_on(chan, note.note.pitch, note.note.vel).timestamp = date_phys;
235 self.running_notes.push_back({{note.note}, date});
240 p2.note_off(chan, note.note.pitch, note.note.vel).timestamp = date_phys;
243 self.to_start.clear();
247 stop_notes(ossia::physical_time date_phys,
int chan, ossia::midi_port& p2,
State&
self)
249 for(
auto& note :
self.running_notes)
251 p2.note_off(chan, note.note.pitch, note.note.vel).timestamp = date_phys;
253 self.running_notes.clear();
Utilities for OSSIA data structures.
Definition: DeviceInterface.hpp:33
Definition: score-lib-process/Control/Widgets.hpp:486
Definition: score-lib-process/Control/Widgets.hpp:77
Definition: SimpleApi.hpp:81
Definition: VelToNote.hpp:52
Definition: VelToNote.hpp:64
Definition: VelToNote.hpp:13