diff --git a/doc/cheatsheet.txt b/doc/cheatsheet.txt index b527bc8..6196972 100644 --- a/doc/cheatsheet.txt +++ b/doc/cheatsheet.txt @@ -111,3 +111,6 @@ play : music... -> nil chord : (collection | music)... -> music tworzy akord z przekazanych wartości muzycznych + +par : x: music -> xs: playable... -> nil + odtwarza dźwięk x równolegle z wszystkimi dźwiękami xs diff --git a/examples/ode-to-joy.mq b/examples/ode-to-joy.mq new file mode 100644 index 0000000..c0f311d --- /dev/null +++ b/examples/ode-to-joy.mq @@ -0,0 +1,15 @@ +var C = c47; +var E = e47; +var G = g47; +var A = a37; + +oct 5; +par (C 4) e e f g; +par (G 4) g f e d; +par (C 4) c c d e; +par (G 4) e d d; +par (C 4) e e f g; +par (G 4) g f e d; +par (C 4) c c d e; +par (G 4) d c; +par (C 4) c diff --git a/src/interpreter.cc b/src/interpreter.cc index c36b61f..cbab82a 100644 --- a/src/interpreter.cc +++ b/src/interpreter.cc @@ -201,6 +201,28 @@ static Result plus_minus_operator(Interpreter &interpreter, std::vector +Result ctx_read_write_property(Interpreter &interpreter, std::vector args) +{ + assert(args.size() <= 1, "Ctx get or set is only supported (wrong number of arguments)"); // TODO(assert) + + using Member_Type = std::remove_cvref_t().*(Mem_Ptr))>; + + if (args.size() == 0) { + return Value::from(Number(interpreter.context_stack.back().*(Mem_Ptr))); + } + + assert(args.front().type == Value::Type::Number, "Ctx only holds numeric values"); + + if constexpr (std::is_same_v) { + interpreter.context_stack.back().*(Mem_Ptr) = args.front().n; + } else { + interpreter.context_stack.back().*(Mem_Ptr) = static_cast(args.front().n.as_int()); + } + + return Value{}; +} + Interpreter::Interpreter() { { // Context initialization @@ -231,7 +253,10 @@ Interpreter::Interpreter() } }); - global.force_define("len", +[](Interpreter &, std::vector args) -> Result { + global.force_define("len", +[](Interpreter &i, std::vector args) -> Result { + if (args.size() != 1 || !is_indexable(args.front().type)) { + return ctx_read_write_property<&Context::length>(i, std::move(args)); + } assert(args.size() == 1, "len only accepts one argument"); assert(args.front().type == Value::Type::Block || args.front().type == Value::Type::Array, "Only blocks and arrays can have length"); return Value::from(Number(args.front().size())); @@ -269,6 +294,35 @@ Interpreter::Interpreter() return Value::from(std::move(chord)); }); + global.force_define("bpm", &ctx_read_write_property<&Context::bpm>); + global.force_define("oct", &ctx_read_write_property<&Context::octave>); + + global.force_define("par", +[](Interpreter &i, std::vector args) -> Result { + assert(args.size() >= 1, "par only makes sense for at least one argument"); // TODO(assert) + if (args.size() == 1) { + i.play(std::move(args.front()).chord); + return Value{}; + } + + 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); }); + + for (auto const& note : chord.notes) { + i.midi_connection->send_note_on(0, *note.into_midi_note(), 127); + } + + for (auto it = std::next(args.begin()); it != args.end(); ++it) { + i.play(std::move(*it).chord); + } + + for (auto const& note : chord.notes) { + i.midi_connection->send_note_off(0, *note.into_midi_note(), 127); + } + + return Value{}; + }); + operators["+"] = plus_minus_operator>; operators["-"] = plus_minus_operator>; operators["*"] = binary_operator>;