Added pause
It's faking to be a note, but does not play anything when played
This commit is contained in:
parent
91f401a13a
commit
0743a9b7c4
@ -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;
|
||||
|
@ -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,14 +294,18 @@ static Result<Value> builtin_par(Interpreter &i, std::vector<Value> args) {
|
||||
std::for_each(chord.notes.begin(), chord.notes.end(), [&](Note ¬e) { note = ctx.fill(note); });
|
||||
|
||||
for (auto const& note : chord.notes) {
|
||||
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) {
|
||||
if (note.base) {
|
||||
i.midi_connection->send_note_off(0, *note.into_midi_note(), 127);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -49,18 +49,22 @@ static Result<Value> plus_minus_operator(Interpreter &interpreter, std::vector<V
|
||||
if (MN::typecheck(args)) {
|
||||
auto [chord, offset] = MN::move_from(args);
|
||||
for (auto ¬e : chord.notes) {
|
||||
note.base = Binary_Operation{}(note.base, offset.as_int());
|
||||
if (note.base) {
|
||||
*note.base = Binary_Operation{}(*note.base, offset.as_int());
|
||||
note.simplify_inplace();
|
||||
}
|
||||
}
|
||||
return Value::from(std::move(chord));
|
||||
}
|
||||
|
||||
if (NM::typecheck(args)) {
|
||||
auto [offset, chord] = NM::move_from(args);
|
||||
for (auto ¬e : chord.notes) {
|
||||
note.base = Binary_Operation{}(offset.as_int(), note.base);
|
||||
if (note.base) {
|
||||
*note.base = Binary_Operation{}(*note.base, offset.as_int());
|
||||
note.simplify_inplace();
|
||||
}
|
||||
}
|
||||
return Value::from(std::move(chord));
|
||||
}
|
||||
|
||||
|
@ -234,8 +234,10 @@ Result<void> Interpreter::play(Chord chord)
|
||||
|
||||
// Turn all notes on
|
||||
for (auto const& note : chord.notes) {
|
||||
if (note.base) {
|
||||
midi_connection->send_note_on(0, *note.into_midi_note(), 127);
|
||||
}
|
||||
}
|
||||
|
||||
// Turn off each note at right time
|
||||
for (auto const& note : chord.notes) {
|
||||
@ -243,8 +245,10 @@ Result<void> Interpreter::play(Chord chord)
|
||||
max_time -= *note.length;
|
||||
std::this_thread::sleep_for(ctx.length_to_duration(*note.length));
|
||||
}
|
||||
if (note.base) {
|
||||
midi_connection->send_note_off(0, *note.into_midi_note(), 127);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
48
src/value.cc
48
src/value.cc
@ -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 (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 (12 * *octave) + *base <=> (12 * *rhs.octave) + *rhs.base;
|
||||
return *base <=> *rhs.base;
|
||||
}
|
||||
}
|
||||
return std::partial_ordering::unordered;
|
||||
}
|
||||
@ -460,10 +474,14 @@ 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.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;
|
||||
|
Loading…
Reference in New Issue
Block a user