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;
|
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;
|
||||||
|
@ -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 ¬e) { note = ctx.fill(note); });
|
std::for_each(chord.notes.begin(), chord.notes.end(), [&](Note ¬e) { 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;
|
||||||
}
|
}
|
||||||
|
@ -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 ¬e : chord.notes) {
|
for (auto ¬e : 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 ¬e : chord.notes) {
|
for (auto ¬e : 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));
|
||||||
}
|
}
|
||||||
|
@ -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 {};
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
56
src/value.cc
56
src/value.cc
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user