diff --git a/include/musique.hh b/include/musique.hh index 41982ba..68e4b3e 100644 --- a/include/musique.hh +++ b/include/musique.hh @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -956,6 +957,8 @@ struct Interpreter /// There is always at least one context std::vector context_stack; + std::function(Interpreter&, Value)> default_action; + struct Incoming_Midi_Callbacks; std::unique_ptr callbacks; void register_callbacks(); diff --git a/src/builtin_functions.cc b/src/builtin_functions.cc index 8ed49ac..65f4884 100644 --- a/src/builtin_functions.cc +++ b/src/builtin_functions.cc @@ -62,7 +62,7 @@ static inline Result create_chord(std::vector &chord, Interpreter &i } template -static inline Result play_notes(Interpreter &interpreter, T args) +static inline Result builtin_play(Interpreter &interpreter, T args) { for (auto i = 0u; i < args.size(); ++i) { Value arg; @@ -75,7 +75,7 @@ static inline Result play_notes(Interpreter &interpreter, T args) switch (arg.type) { case Value::Type::Array: case Value::Type::Block: - Try(play_notes(interpreter, std::move(arg))); + Try(builtin_play(interpreter, std::move(arg))); break; case Value::Type::Music: @@ -238,6 +238,17 @@ static auto builtin_program_change(Interpreter &i, std::vector args) -> R }; } +static Result action_play(Interpreter &i, Value v) +{ + if (v.type == Value::Type::Music) { + Try(i.play(std::move(v).chord)); + } + return {}; +} + +/// Plays sequentialy notes walking into arrays and evaluation blocks +/// +/// @invariant default_action is play one static inline Result sequential_play(Interpreter &i, Value v) { switch (v.type) { @@ -246,7 +257,7 @@ static inline Result sequential_play(Interpreter &i, Value v) Try(sequential_play(i, std::move(el))); break; case Value::Type::Block: - unimplemented(); // waits for eval support + Try(sequential_play(i, Try(i.eval(std::move(v).blk.body)))); break; case Value::Type::Music: return i.play(v.chord); @@ -270,16 +281,21 @@ static Result builtin_par(Interpreter &i, std::vector args) { // Create chord that should sustain during playing of all other notes auto &ctx = i.context_stack.back(); auto chord = std::move(args.front()).chord; - std::transform(chord.notes.begin(), chord.notes.end(), chord.notes.begin(), [&](Note note) { return ctx.fill(note); }); + std::for_each(chord.notes.begin(), chord.notes.end(), [&](Note ¬e) { note = ctx.fill(note); }); for (auto const& note : chord.notes) { i.midi_connection->send_note_on(0, *note.into_midi_note(), 127); } + auto previous_action = std::exchange(i.default_action, action_play); + i.context_stack.push_back(i.context_stack.back()); + auto const chord_off = [&] { for (auto const& note : chord.notes) { i.midi_connection->send_note_off(0, *note.into_midi_note(), 127); } + i.default_action = std::move(previous_action); + i.context_stack.pop_back(); }; for (auto it = std::next(args.begin()); it != args.end(); ++it) { @@ -368,7 +384,7 @@ error: }); global.force_define("play", +[](Interpreter &i, std::vector args) -> Result { - Try(play_notes(i, std::move(args))); + Try(builtin_play(i, std::move(args))); return Value{}; }); diff --git a/src/interpreter.cc b/src/interpreter.cc index a320946..583bf8e 100644 --- a/src/interpreter.cc +++ b/src/interpreter.cc @@ -142,8 +142,12 @@ Result Interpreter::eval(Ast &&ast) case Ast::Type::Sequence: { Value v; - for (auto &a : ast.arguments) + bool first = true; + for (auto &a : ast.arguments) { + if (!first && default_action) Try(default_action(*this, v)); v = Try(eval(std::move(a))); + first = false; + } return v; }