Added pause

It's faking to be a note, but does not play anything when played
This commit is contained in:
Robert Bendun 2022-09-11 16:19:10 +02:00
parent 91f401a13a
commit 0743a9b7c4
7 changed files with 65 additions and 31 deletions

View File

@ -761,11 +761,12 @@ struct Block
usize size() const; usize size() const;
}; };
/// Representation of musical note /// Representation of musical note or musical pause
struct Note struct Note
{ {
/// Base of a note, like `c` (=0), `c#` (=1) `d` (=2) /// Base of a note, like `c` (=0), `c#` (=1) `d` (=2)
i32 base; /// Or nullopt where there is no note - case when we have pause
std::optional<i32> base = std::nullopt;
/// Octave in MIDI acceptable range (from -1 to 9 inclusive) /// Octave in MIDI acceptable range (from -1 to 9 inclusive)
std::optional<i8> octave = std::nullopt; std::optional<i8> octave = std::nullopt;

View File

@ -50,7 +50,7 @@ static inline Result<void> create_chord(std::vector<Note> &chord, Interpreter &i
break; break;
case Value::Type::Music: case Value::Type::Music:
std::move(arg.chord.notes.begin(), arg.chord.notes.end(), std::back_inserter(chord)); std::copy_if(arg.chord.notes.begin(), arg.chord.notes.end(), std::back_inserter(chord), [](Note const& n) { return bool(n.base); });
break; break;
default: default:
@ -294,13 +294,17 @@ static Result<Value> builtin_par(Interpreter &i, std::vector<Value> args) {
std::for_each(chord.notes.begin(), chord.notes.end(), [&](Note &note) { note = ctx.fill(note); }); std::for_each(chord.notes.begin(), chord.notes.end(), [&](Note &note) { note = ctx.fill(note); });
for (auto const& note : chord.notes) { for (auto const& note : chord.notes) {
i.midi_connection->send_note_on(0, *note.into_midi_note(), 127); if (note.base) {
i.midi_connection->send_note_on(0, *note.into_midi_note(), 127);
}
} }
auto result = builtin_play(i, std::span(args).subspan(1)); auto result = builtin_play(i, std::span(args).subspan(1));
for (auto const& note : chord.notes) { for (auto const& note : chord.notes) {
i.midi_connection->send_note_off(0, *note.into_midi_note(), 127); if (note.base) {
i.midi_connection->send_note_off(0, *note.into_midi_note(), 127);
}
} }
return result; return result;
} }

View File

@ -49,8 +49,10 @@ static Result<Value> plus_minus_operator(Interpreter &interpreter, std::vector<V
if (MN::typecheck(args)) { if (MN::typecheck(args)) {
auto [chord, offset] = MN::move_from(args); auto [chord, offset] = MN::move_from(args);
for (auto &note : chord.notes) { for (auto &note : chord.notes) {
note.base = Binary_Operation{}(note.base, offset.as_int()); if (note.base) {
note.simplify_inplace(); *note.base = Binary_Operation{}(*note.base, offset.as_int());
note.simplify_inplace();
}
} }
return Value::from(std::move(chord)); return Value::from(std::move(chord));
} }
@ -58,8 +60,10 @@ static Result<Value> plus_minus_operator(Interpreter &interpreter, std::vector<V
if (NM::typecheck(args)) { if (NM::typecheck(args)) {
auto [offset, chord] = NM::move_from(args); auto [offset, chord] = NM::move_from(args);
for (auto &note : chord.notes) { for (auto &note : chord.notes) {
note.base = Binary_Operation{}(offset.as_int(), note.base); if (note.base) {
note.simplify_inplace(); *note.base = Binary_Operation{}(*note.base, offset.as_int());
note.simplify_inplace();
}
} }
return Value::from(std::move(chord)); return Value::from(std::move(chord));
} }

View File

@ -234,7 +234,9 @@ Result<void> Interpreter::play(Chord chord)
// Turn all notes on // Turn all notes on
for (auto const& note : chord.notes) { for (auto const& note : chord.notes) {
midi_connection->send_note_on(0, *note.into_midi_note(), 127); if (note.base) {
midi_connection->send_note_on(0, *note.into_midi_note(), 127);
}
} }
// Turn off each note at right time // Turn off each note at right time
@ -243,7 +245,9 @@ Result<void> Interpreter::play(Chord chord)
max_time -= *note.length; max_time -= *note.length;
std::this_thread::sleep_for(ctx.length_to_duration(*note.length)); std::this_thread::sleep_for(ctx.length_to_duration(*note.length));
} }
midi_connection->send_note_off(0, *note.into_midi_note(), 127); if (note.base) {
midi_connection->send_note_off(0, *note.into_midi_note(), 127);
}
} }
return {}; return {};

View File

@ -3,7 +3,7 @@
#include <iomanip> #include <iomanip>
constexpr std::string_view Notes_Symbols = "abcedefgh"; constexpr std::string_view Notes_Symbols = "abcedefghp";
constexpr std::string_view Valid_Operator_Chars = constexpr std::string_view Valid_Operator_Chars =
"+-*/:%" // arithmetic "+-*/:%" // arithmetic
"|&^" // logic & bit operations "|&^" // logic & bit operations

View File

@ -531,6 +531,9 @@ static usize precedense(std::string_view op)
if (one_of(op, "*", "/", "%", "&")) return 400; if (one_of(op, "*", "/", "%", "&")) return 400;
if (one_of(op, "**")) return 500; if (one_of(op, "**")) return 500;
std::cerr << op << std::endl;
unreachable(); unreachable();
} }

View File

@ -17,14 +17,13 @@ constexpr u8 note_index(u8 note)
case 'h': return 11; case 'h': return 11;
case 'b': return 11; case 'b': return 11;
} }
// This should be unreachable since parser limits what character can pass as notes // Parser should limit range of characters that is called with this function
// but just to be sure return special value unreachable();
return -1;
} }
#include <iostream> #include <iostream>
constexpr std::string_view note_index_to_string(auto note_index) constexpr std::string_view note_index_to_string(int note_index)
{ {
note_index %= 12; note_index %= 12;
if (note_index < 0) { if (note_index < 0) {
@ -411,12 +410,17 @@ std::ostream& operator<<(std::ostream& os, Array const& v)
std::optional<Note> Note::from(std::string_view literal) std::optional<Note> Note::from(std::string_view literal)
{ {
if (literal.starts_with('p')) {
return Note {};
}
if (auto const base = note_index(literal[0]); base != u8(-1)) { if (auto const base = note_index(literal[0]); base != u8(-1)) {
Note note { .base = base }; Note note { .base = base };
while (literal.remove_prefix(1), not literal.empty()) { while (literal.remove_prefix(1), not literal.empty()) {
switch (literal.front()) { switch (literal.front()) {
case '#': case 's': ++note.base; break; case '#': case 's': ++*note.base; break;
case 'b': case 'f': --note.base; break; case 'b': case 'f': --*note.base; break;
default: return note; default: return note;
} }
} }
@ -432,27 +436,37 @@ std::optional<u8> Note::into_midi_note() const
u8 Note::into_midi_note(i8 default_octave) const u8 Note::into_midi_note(i8 default_octave) const
{ {
assert(bool(this->base), "Pause don't translate into MIDI");
auto const octave = this->octave.has_value() ? *this->octave : default_octave; auto const octave = this->octave.has_value() ? *this->octave : default_octave;
// octave is in range [-1, 9] where Note { .base = 0, .octave = -1 } is midi note 0 // octave is in range [-1, 9] where Note { .base = 0, .octave = -1 } is midi note 0
return (octave + 1) * 12 + base; return (octave + 1) * 12 + *base;
} }
void Note::simplify_inplace() void Note::simplify_inplace()
{ {
if (octave) { if (base && octave) {
octave = std::clamp(*octave + int(base / 12), -1, 9); octave = std::clamp(*octave + int(*base / 12), -1, 9);
if ((base %= 12) < 0) { if ((*base %= 12) < 0) {
base = 12 + base; base = 12 + *base;
} }
} }
} }
std::partial_ordering Note::operator<=>(Note const& rhs) const std::partial_ordering Note::operator<=>(Note const& rhs) const
{ {
if (octave.has_value() == rhs.octave.has_value()) { if (base.has_value() == rhs.base.has_value()) {
if (octave.has_value()) if (!base.has_value()) {
return (12 * *octave) + base <=> (12 * *rhs.octave) + rhs.base; if (length.has_value() == rhs.length.has_value() && length.has_value()) {
return base <=> rhs.base; return *length <=> *rhs.length;
}
return std::partial_ordering::unordered;
}
if (octave.has_value() == rhs.octave.has_value()) {
if (octave.has_value())
return (12 * *octave) + *base <=> (12 * *rhs.octave) + *rhs.base;
return *base <=> *rhs.base;
}
} }
return std::partial_ordering::unordered; return std::partial_ordering::unordered;
} }
@ -460,9 +474,13 @@ std::partial_ordering Note::operator<=>(Note const& rhs) const
std::ostream& operator<<(std::ostream& os, Note note) std::ostream& operator<<(std::ostream& os, Note note)
{ {
note.simplify_inplace(); note.simplify_inplace();
os << note_index_to_string(note.base); if (note.base) {
if (note.octave) { os << note_index_to_string(*note.base);
os << ":oct=" << int(*note.octave); if (note.octave) {
os << ":oct=" << int(*note.octave);
}
} else {
os << "p";
} }
if (note.length) { if (note.length) {
os << ":len=" << *note.length; os << ":len=" << *note.length;
@ -544,7 +562,7 @@ std::size_t std::hash<Value>::operator()(Value const& value) const
break; case Value::Type::Music: break; case Value::Type::Music:
value_hash = std::accumulate(value.chord.notes.begin(), value.chord.notes.end(), value_hash, [](size_t h, Note const& n) { value_hash = std::accumulate(value.chord.notes.begin(), value.chord.notes.end(), value_hash, [](size_t h, Note const& n) {
h = hash_combine(h, n.base); h = hash_combine(h, std::hash<std::optional<int>>{}(n.base));
h = hash_combine(h, std::hash<std::optional<Number>>{}(n.length)); h = hash_combine(h, std::hash<std::optional<Number>>{}(n.length));
h = hash_combine(h, std::hash<std::optional<i8>>{}(n.octave)); h = hash_combine(h, std::hash<std::optional<i8>>{}(n.octave));
return h; return h;