Playing MIDI!

This commit is contained in:
Robert Bendun 2022-05-24 03:35:15 +02:00
parent 5560549c7c
commit 2c19db7354
4 changed files with 80 additions and 21 deletions

View File

@ -1,6 +1,8 @@
#include <musique.hh>
#include <chrono>
#include <iostream>
#include <thread>
template<typename Binary_Operation>
constexpr auto binary_operator()
@ -110,6 +112,14 @@ Interpreter::Interpreter(std::ostream& out)
}
});
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);
}
return Value{};
});
operators["+"] = binary_operator<std::plus<>>();
operators["-"] = binary_operator<std::minus<>>();
operators["*"] = binary_operator<std::multiplies<>>();
@ -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);
}

View File

@ -1,10 +1,11 @@
#define MIDI_ENABLE_ALSA_SUPPORT
#include <filesystem>
#include <fstream>
#include <iostream>
#include <span>
#include <musique.hh>
#define MIDI_ENABLE_ALSA_SUPPORT
#include <midi.hh>
namespace fs = std::filesystem;
@ -34,7 +35,20 @@ void usage()
std::exit(1);
}
static Result<void> run(std::string_view source, std::string_view filename)
struct Runner
{
midi::ALSA alsa;
Interpreter interpreter;
Runner(std::string port)
: alsa("musique"), interpreter(std::cout)
{
alsa.init_sequencer();
alsa.connect(port);
interpreter.midi_connection = &alsa;
}
Result<void> run(std::string_view source, std::string_view filename)
{
auto ast = Try(Parser::parse(source, filename));
@ -42,28 +56,33 @@ static Result<void> run(std::string_view source, std::string_view filename)
dump(ast);
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<std::string> eternal_sources;
struct Run
{
bool is_file = true;
std::string_view argument;
};
static Result<void> Main(std::span<char const*> args)
{
if (args.empty()) {
usage();
}
bool runned_something = false;
std::vector<std::string_view> files;
std::vector<Run> 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<void> Main(std::span<char const*> 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<void> Main(std::span<char const*> 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, "<arguments>"));
continue;
}
auto const path = argument;
if (path == "-") {
eternal_sources.emplace_back(std::istreambuf_iterator<char>(std::cin), std::istreambuf_iterator<char>());
} else {
@ -111,7 +134,7 @@ static Result<void> Main(std::span<char const*> args)
eternal_sources.emplace_back(std::istreambuf_iterator<char>(source_file), std::istreambuf_iterator<char>());
}
Try(run(eternal_sources.back(), path));
Try(runner.run(eternal_sources.back(), path));
}
return {};

View File

@ -9,9 +9,11 @@
#include <ostream>
#include <span>
#include <string_view>
#include <tl/expected.hpp>
#include <variant>
#include <midi.hh>
#include <tl/expected.hpp>
#if defined(__cpp_lib_source_location)
#include <source_location>
#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

View File

@ -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<usize, char> 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