diff --git a/src/interpreter.cc b/src/interpreter.cc index 748f733..363fa52 100644 --- a/src/interpreter.cc +++ b/src/interpreter.cc @@ -1,6 +1,8 @@ #include +#include #include +#include template constexpr auto binary_operator() @@ -110,6 +112,14 @@ Interpreter::Interpreter(std::ostream& out) } }); + global.force_define("play", +[](Interpreter &i, std::vector args) -> Result { + for (auto &arg : args) { + assert(arg.type == Value::Type::Music, "Only music values can be played"); // TODO(assert) + i.play(arg.note); + } + return Value{}; + }); + operators["+"] = binary_operator>(); operators["-"] = binary_operator>(); operators["*"] = binary_operator>(); @@ -230,3 +240,17 @@ void Interpreter::leave_scope() assert(env != Env::global, "Cannot leave global scope"); env = env->leave(); } + +void Interpreter::play(Note note) +{ + 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); + + midi_connection->send_note_on(0, *note.into_midi_note(), 127); + std::this_thread::sleep_for(dur); + midi_connection->send_note_off(0, *note.into_midi_note(), 127); +} diff --git a/src/main.cc b/src/main.cc index 93a49e1..0086dcc 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,10 +1,11 @@ +#define MIDI_ENABLE_ALSA_SUPPORT + #include #include #include #include #include -#define MIDI_ENABLE_ALSA_SUPPORT #include namespace fs = std::filesystem; @@ -34,36 +35,54 @@ void usage() std::exit(1); } -static Result run(std::string_view source, std::string_view filename) +struct Runner { - auto ast = Try(Parser::parse(source, filename)); + midi::ALSA alsa; + Interpreter interpreter; - if (ast_only_mode) { - dump(ast); + Runner(std::string port) + : alsa("musique"), interpreter(std::cout) + { + alsa.init_sequencer(); + alsa.connect(port); + interpreter.midi_connection = &alsa; + } + + Result run(std::string_view source, std::string_view filename) + { + auto ast = Try(Parser::parse(source, filename)); + + if (ast_only_mode) { + dump(ast); + return {}; + } + std::cout << Try(interpreter.eval(std::move(ast))) << std::endl; return {}; } - Interpreter interpreter; - std::cout << Try(interpreter.eval(std::move(ast))) << std::endl; - return {}; -} +}; // We make sure that through life of interpreter source code is allways allocated std::vector eternal_sources; +struct Run +{ + bool is_file = true; + std::string_view argument; +}; + static Result Main(std::span args) { if (args.empty()) { usage(); } - bool runned_something = false; - std::vector files; + std::vector runnables; while (not args.empty()) { std::string_view arg = pop(args); if (arg == "-" || !arg.starts_with('-')) { - files.push_back(std::move(arg)); + runnables.push_back({ .is_file = true, .argument = std::move(arg) }); continue; } @@ -79,10 +98,7 @@ static Result Main(std::span args) std::cerr << "musique: error: option " << arg << " requires an argument" << std::endl; std::exit(1); } - - auto const source = pop(args); - Try(run(source, "arguments")); - runned_something = true; + runnables.push_back({ .is_file = false, .argument = pop(args) }); continue; } @@ -95,11 +111,18 @@ static Result Main(std::span args) std::exit(1); } - if (!runned_something && files.empty()) { + if (runnables.empty()) { usage(); } - for (auto const& path : files) { + Runner runner("14"); + + for (auto const& [is_file, argument] : runnables) { + if (!is_file) { + Try(runner.run(argument, "")); + continue; + } + auto const path = argument; if (path == "-") { eternal_sources.emplace_back(std::istreambuf_iterator(std::cin), std::istreambuf_iterator()); } else { @@ -111,7 +134,7 @@ static Result Main(std::span args) eternal_sources.emplace_back(std::istreambuf_iterator(source_file), std::istreambuf_iterator()); } - Try(run(eternal_sources.back(), path)); + Try(runner.run(eternal_sources.back(), path)); } return {}; diff --git a/src/musique.hh b/src/musique.hh index 185b917..5ccdd9d 100644 --- a/src/musique.hh +++ b/src/musique.hh @@ -9,9 +9,11 @@ #include #include #include -#include #include +#include +#include + #if defined(__cpp_lib_source_location) #include #endif @@ -579,6 +581,10 @@ struct Context struct Interpreter { + /// MIDI connection that is used to play music. + /// It's optional for simple interpreter testing. + midi::Connection *midi_connection = nullptr; + /// Output of IO builtins like `say` std::ostream &out; @@ -603,6 +609,9 @@ struct Interpreter // Scope managment void enter_scope(); void leave_scope(); + + /// Play note resolving any missing parameters with context via `midi_connection` member. + void play(Note n); }; namespace errors diff --git a/src/value.cc b/src/value.cc index bbac5eb..b2dc08d 100644 --- a/src/value.cc +++ b/src/value.cc @@ -8,7 +8,10 @@ concept Indexable = requires(T t, Index i) { /// Create hash out of note literal like `c` or `e#` constexpr u16 hash_note(Indexable auto const& note) { - return u8(note[0]) | (note[1] << 8); + /// TODO Some assertion that we have snd character + u8 snd = note[1]; + if (snd != '#') snd = 0; + return u8(note[0]) | (snd << 8); } /// Finds numeric value of note. This form is later used as in