This commit is contained in:
Robert Bendun 2022-05-25 00:08:48 +02:00
parent 6611512b1f
commit 80bc4c039c
4 changed files with 89 additions and 36 deletions

View File

@ -113,7 +113,7 @@ Interpreter::Interpreter()
global.force_define("play", +[](Interpreter &i, std::vector<Value> args) -> Result<Value> {
for (auto &arg : args) {
assert(arg.type == Value::Type::Music, "Only music values can be played"); // TODO(assert)
i.play(arg.note);
i.play(arg.chord);
}
return Value{};
});
@ -264,16 +264,31 @@ void Interpreter::leave_scope()
env = env->leave();
}
void Interpreter::play(Note note)
void Interpreter::play(Chord chord)
{
assert(midi_connection, "To play midi Interpreter requires instance of MIDI connection");
auto &ctx = context_stack.back();
note = ctx.fill(std::move(note));
auto dur = ctx.length_to_duration(note.length);
// Fill all notes that don't have octave or length with defaults
std::transform(chord.notes.begin(), chord.notes.end(), chord.notes.begin(), [&](Note note) { return ctx.fill(note); });
// Sort that we have smaller times first
std::sort(chord.notes.begin(), chord.notes.end(), [](Note const& lhs, Note const& rhs) { return lhs.length < rhs.length; });
Number max_time = *chord.notes.back().length;
// Turn all notes on
for (auto const& note : chord.notes) {
midi_connection->send_note_on(0, *note.into_midi_note(), 127);
std::this_thread::sleep_for(dur);
}
// Turn off each note at right time
for (auto const& note : chord.notes) {
if (max_time != Number(0)) {
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);
}
}

View File

@ -105,12 +105,7 @@ auto Lexer::next_token() -> Result<Token>
// Allow `c#`
consume_if('#');
// Any of the following sequences are allowed
// c,,,,,,,,,,,,,,,,
// c1,,,,2,3212
// c1234'''''
// during lexing
while (consume_if(",'") || consume_if(unicode::is_digit)) {}
while (consume_if('_') || consume_if(unicode::is_digit)) {}
// If we encounter any letter that is not part of chord declaration,
// then we have symbol, not chord declaration
@ -125,9 +120,7 @@ auto Lexer::next_token() -> Result<Token>
if (consume_if(std::bind(unicode::is_identifier, _1, unicode::First_Character::Yes))) {
symbol_lexing:
for (auto predicate = std::bind(unicode::is_identifier, _1, unicode::First_Character::No);
consume_if(predicate);
) {
}
consume_if(predicate);) {}
Token t = { Token::Type::Symbol, finish(), token_location };
if (std::find(Keywords.begin(), Keywords.end(), t.source) != Keywords.end()) {

View File

@ -494,6 +494,17 @@ struct Note
std::ostream& operator<<(std::ostream& os, Note const& note);
struct Chord
{
std::vector<Note> notes;
static Chord from(std::string_view source);
bool operator==(Chord const&) const = default;
};
std::ostream& operator<<(std::ostream& os, Chord const& chord);
/// Eager Array
struct Array
{
@ -523,6 +534,7 @@ struct Value
static Value from(Block &&l);
static Value from(Array &&array);
static Value from(Note n);
static Value from(Chord chord);
enum class Type
{
@ -533,7 +545,7 @@ struct Value
Intrinsic,
Block,
Array,
Music
Music,
};
Value() = default;
@ -551,7 +563,7 @@ struct Value
Number n;
Intrinsic intr;
Block blk;
Note note;
Chord chord;
Array array;
// TODO Most strings should not be allocated by Value, but reference to string allocated previously
@ -647,7 +659,7 @@ struct Interpreter
void leave_scope();
/// Play note resolving any missing parameters with context via `midi_connection` member.
void play(Note n);
void play(Chord);
};
namespace errors

View File

@ -83,7 +83,7 @@ Result<Value> Value::from(Token t)
return Value::from(*maybe_note);
}
unimplemented("only simple note values (like c or e#) are supported now");
return Value::from(Chord::from(t.source));
default:
unimplemented();
@ -150,7 +150,15 @@ Value Value::from(Note n)
{
Value v;
v.type = Type::Music;
v.note = n;
v.chord = { .notes = { n } };
return v;
}
Value Value::from(Chord chord)
{
Value v;
v.type = Type::Music;
v.chord = std::move(chord);
return v;
}
@ -164,12 +172,13 @@ Result<Value> Value::operator()(Interpreter &i, std::vector<Value> args)
assert(args.size() == 1 || args.size() == 2, "music value can be called only in form note <octave> [<length>]"); // TODO(assert)
assert(args[0].type == Type::Number, "expected octave to be a number"); // TODO(assert)
assert(args.size() == 2 ? args[1].type == Type::Number : true, "expected length to be a number"); // TODO(assert)
for (auto &note : chord.notes) {
note.octave = args[0].n.as_int();
if (args.size() == 2) {
assert(args[1].type == Type::Number, "expected length to be a number"); // TODO(assert)
note.length = args[1].n;
}
}
return *this;
}
@ -224,7 +233,7 @@ bool Value::operator==(Value const& other) const
case Type::Intrinsic: return intr == other.intr;
case Type::Block: return false; // TODO Reconsider if functions are comparable
case Type::Bool: return b == other.b;
case Type::Music: return note == other.note;
case Type::Music: return chord == other.chord;
case Type::Array: return array == other.array;
}
@ -272,7 +281,7 @@ std::ostream& operator<<(std::ostream& os, Value const& v)
return os << v.array;
case Value::Type::Music:
return os << v.note;
return os << v.chord;
}
unreachable();
}
@ -374,18 +383,13 @@ u8 Note::into_midi_note(i8 default_octave) const
std::ostream& operator<<(std::ostream& os, Note const& note)
{
os << note_index_to_string(note.base);
os << ":oct=";
if (note.octave) {
os << int(*note.octave);
} else {
os << '_';
os << ":oct=" << int(*note.octave);
}
os << ":len=";
if (note.length) {
os << *note.length;
} else {
os << '_';
os << ":len=" << *note.length;
}
return os;
}
@ -393,3 +397,32 @@ bool Note::operator==(Note const& other) const
{
return octave == other.octave && base == other.base && length == other.length;
}
Chord Chord::from(std::string_view source)
{
auto note = Note::from(source);
assert(note.has_value(), "don't know how this could happen");
Chord chord;
source.remove_prefix(1 + (source[1] == '#'));
chord.notes.push_back(*std::move(note));
for (char digit : source) {
chord.notes.push_back(Note { .base = u8(digit - '0') });
}
return chord;
}
std::ostream& operator<<(std::ostream& os, Chord const& chord)
{
os << "chord[";
for (auto it = chord.notes.begin(); it != chord.notes.end(); ++it) {
os << *it;
if (std::next(it) != chord.notes.end())
os << "; ";
}
return os << ']';
}