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;
};
/// Representation of musical note
/// Representation of musical note or musical pause
struct Note
{
/// 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)
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;
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;
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); });
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));
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;
}

View File

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

View File

@ -234,7 +234,9 @@ Result<void> Interpreter::play(Chord chord)
// Turn all notes on
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
@ -243,7 +245,9 @@ Result<void> Interpreter::play(Chord chord)
max_time -= *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 {};

View File

@ -3,7 +3,7 @@
#include <iomanip>
constexpr std::string_view Notes_Symbols = "abcedefgh";
constexpr std::string_view Notes_Symbols = "abcedefghp";
constexpr std::string_view Valid_Operator_Chars =
"+-*/:%" // arithmetic
"|&^" // 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 500;
std::cerr << op << std::endl;
unreachable();
}

View File

@ -17,14 +17,13 @@ constexpr u8 note_index(u8 note)
case 'h': return 11;
case 'b': return 11;
}
// This should be unreachable since parser limits what character can pass as notes
// but just to be sure return special value
return -1;
// Parser should limit range of characters that is called with this function
unreachable();
}
#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;
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)
{
if (literal.starts_with('p')) {
return Note {};
}
if (auto const base = note_index(literal[0]); base != u8(-1)) {
Note note { .base = base };
while (literal.remove_prefix(1), not literal.empty()) {
switch (literal.front()) {
case '#': case 's': ++note.base; break;
case 'b': case 'f': --note.base; break;
case '#': case 's': ++*note.base; break;
case 'b': case 'f': --*note.base; break;
default: return note;
}
}
@ -432,27 +436,37 @@ std::optional<u8> Note::into_midi_note() 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;
// 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()
{
if (octave) {
octave = std::clamp(*octave + int(base / 12), -1, 9);
if ((base %= 12) < 0) {
base = 12 + base;
if (base && octave) {
octave = std::clamp(*octave + int(*base / 12), -1, 9);
if ((*base %= 12) < 0) {
base = 12 + *base;
}
}
}
std::partial_ordering Note::operator<=>(Note const& rhs) const
{
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;
if (base.has_value() == rhs.base.has_value()) {
if (!base.has_value()) {
if (length.has_value() == rhs.length.has_value() && length.has_value()) {
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;
}
@ -460,9 +474,13 @@ std::partial_ordering Note::operator<=>(Note const& rhs) const
std::ostream& operator<<(std::ostream& os, Note note)
{
note.simplify_inplace();
os << note_index_to_string(note.base);
if (note.octave) {
os << ":oct=" << int(*note.octave);
if (note.base) {
os << note_index_to_string(*note.base);
if (note.octave) {
os << ":oct=" << int(*note.octave);
}
} else {
os << "p";
}
if (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:
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<i8>>{}(n.octave));
return h;