Playing MIDI!
This commit is contained in:
parent
5560549c7c
commit
2c19db7354
@ -1,6 +1,8 @@
|
|||||||
#include <musique.hh>
|
#include <musique.hh>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
template<typename Binary_Operation>
|
template<typename Binary_Operation>
|
||||||
constexpr auto binary_operator()
|
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::plus<>>();
|
||||||
operators["-"] = binary_operator<std::minus<>>();
|
operators["-"] = binary_operator<std::minus<>>();
|
||||||
operators["*"] = binary_operator<std::multiplies<>>();
|
operators["*"] = binary_operator<std::multiplies<>>();
|
||||||
@ -230,3 +240,17 @@ void Interpreter::leave_scope()
|
|||||||
assert(env != Env::global, "Cannot leave global scope");
|
assert(env != Env::global, "Cannot leave global scope");
|
||||||
env = env->leave();
|
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);
|
||||||
|
}
|
||||||
|
61
src/main.cc
61
src/main.cc
@ -1,10 +1,11 @@
|
|||||||
|
#define MIDI_ENABLE_ALSA_SUPPORT
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <span>
|
#include <span>
|
||||||
|
|
||||||
#include <musique.hh>
|
#include <musique.hh>
|
||||||
#define MIDI_ENABLE_ALSA_SUPPORT
|
|
||||||
#include <midi.hh>
|
#include <midi.hh>
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
@ -34,36 +35,54 @@ void usage()
|
|||||||
std::exit(1);
|
std::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Result<void> 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) {
|
Runner(std::string port)
|
||||||
dump(ast);
|
: 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));
|
||||||
|
|
||||||
|
if (ast_only_mode) {
|
||||||
|
dump(ast);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
std::cout << Try(interpreter.eval(std::move(ast))) << std::endl;
|
||||||
return {};
|
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
|
// We make sure that through life of interpreter source code is allways allocated
|
||||||
std::vector<std::string> eternal_sources;
|
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)
|
static Result<void> Main(std::span<char const*> args)
|
||||||
{
|
{
|
||||||
if (args.empty()) {
|
if (args.empty()) {
|
||||||
usage();
|
usage();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool runned_something = false;
|
std::vector<Run> runnables;
|
||||||
std::vector<std::string_view> files;
|
|
||||||
|
|
||||||
while (not args.empty()) {
|
while (not args.empty()) {
|
||||||
std::string_view arg = pop(args);
|
std::string_view arg = pop(args);
|
||||||
|
|
||||||
if (arg == "-" || !arg.starts_with('-')) {
|
if (arg == "-" || !arg.starts_with('-')) {
|
||||||
files.push_back(std::move(arg));
|
runnables.push_back({ .is_file = true, .argument = std::move(arg) });
|
||||||
continue;
|
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::cerr << "musique: error: option " << arg << " requires an argument" << std::endl;
|
||||||
std::exit(1);
|
std::exit(1);
|
||||||
}
|
}
|
||||||
|
runnables.push_back({ .is_file = false, .argument = pop(args) });
|
||||||
auto const source = pop(args);
|
|
||||||
Try(run(source, "arguments"));
|
|
||||||
runned_something = true;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,11 +111,18 @@ static Result<void> Main(std::span<char const*> args)
|
|||||||
std::exit(1);
|
std::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!runned_something && files.empty()) {
|
if (runnables.empty()) {
|
||||||
usage();
|
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 == "-") {
|
if (path == "-") {
|
||||||
eternal_sources.emplace_back(std::istreambuf_iterator<char>(std::cin), std::istreambuf_iterator<char>());
|
eternal_sources.emplace_back(std::istreambuf_iterator<char>(std::cin), std::istreambuf_iterator<char>());
|
||||||
} else {
|
} 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>());
|
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 {};
|
return {};
|
||||||
|
@ -9,9 +9,11 @@
|
|||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <tl/expected.hpp>
|
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
|
#include <midi.hh>
|
||||||
|
#include <tl/expected.hpp>
|
||||||
|
|
||||||
#if defined(__cpp_lib_source_location)
|
#if defined(__cpp_lib_source_location)
|
||||||
#include <source_location>
|
#include <source_location>
|
||||||
#endif
|
#endif
|
||||||
@ -579,6 +581,10 @@ struct Context
|
|||||||
|
|
||||||
struct Interpreter
|
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`
|
/// Output of IO builtins like `say`
|
||||||
std::ostream &out;
|
std::ostream &out;
|
||||||
|
|
||||||
@ -603,6 +609,9 @@ struct Interpreter
|
|||||||
// Scope managment
|
// Scope managment
|
||||||
void enter_scope();
|
void enter_scope();
|
||||||
void leave_scope();
|
void leave_scope();
|
||||||
|
|
||||||
|
/// Play note resolving any missing parameters with context via `midi_connection` member.
|
||||||
|
void play(Note n);
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace errors
|
namespace errors
|
||||||
|
@ -8,7 +8,10 @@ concept Indexable = requires(T t, Index i) {
|
|||||||
/// Create hash out of note literal like `c` or `e#`
|
/// Create hash out of note literal like `c` or `e#`
|
||||||
constexpr u16 hash_note(Indexable<usize, char> auto const& note)
|
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
|
/// Finds numeric value of note. This form is later used as in
|
||||||
|
Loading…
Reference in New Issue
Block a user