From b93a236e376d18870eff4176bdfcf06965f4f35c Mon Sep 17 00:00:00 2001 From: Robert Bendun Date: Fri, 14 Oct 2022 14:40:01 +0200 Subject: [PATCH 1/4] Conforming to clang's standard library on MacOS --- Makefile | 2 +- config.mk | 8 +++--- musique/algo.hh | 19 ++++++++++++- musique/common.hh | 5 ++++ musique/errors.cc | 2 +- musique/errors.hh | 7 +---- musique/format.cc | 2 +- musique/interpreter/builtin_functions.cc | 34 +++++++++++------------ musique/interpreter/builtin_operators.cc | 6 ++-- musique/interpreter/env.cc | 2 +- musique/interpreter/interpreter.cc | 24 ++++++++-------- musique/interpreter/interpreter.hh | 1 + musique/lexer/lexer.cc | 2 +- musique/lexer/lines.cc | 2 +- musique/main.cc | 23 +++------------- musique/midi/midi.hh | 3 -- musique/midi/rt_midi.cc | 11 ++++---- musique/parser/parser.cc | 10 +++---- musique/try.hh | 8 +++--- musique/value/block.cc | 2 +- musique/value/chord.cc | 4 +-- musique/value/function.hh | 1 + musique/value/intrinsic.hh | 2 ++ musique/value/note.cc | 2 +- musique/value/number.cc | 4 +-- musique/value/value.cc | 35 ++++++++++++++++-------- 26 files changed, 118 insertions(+), 103 deletions(-) diff --git a/Makefile b/Makefile index c0fa0b5..7744d55 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ include scripts/windows.mk # http://www.music.mcgill.ca/~gary/rtmidi/#compiling bin/rtmidi.o: lib/rtmidi/RtMidi.cpp lib/rtmidi/RtMidi.h @echo "CXX $@" - @$(CXX) $< -c -O2 -o $@ $(CPPFLAGS) + @$(CXX) $< -c -O2 -o $@ $(CPPFLAGS) -std=c++20 doc: Doxyfile musique/*.cc musique/*.hh doxygen diff --git a/config.mk b/config.mk index 514be89..4bb0e4a 100644 --- a/config.mk +++ b/config.mk @@ -1,9 +1,9 @@ MAKEFLAGS="-j $(grep -c ^processor /proc/cpuinfo)" -CXXFLAGS:=$(CXXFLAGS) -std=c++20 -Wall -Wextra -Werror=switch -Werror=return-type -Werror=unused-result -Wno-maybe-uninitialized +CXXFLAGS:=$(CXXFLAGS) -std=c++20 -Wall -Wextra -Werror=switch -Werror=return-type -Werror=unused-result CPPFLAGS:=$(CPPFLAGS) -Ilib/expected/ -I. -Ilib/bestline/ -Ilib/rtmidi/ LDFLAGS=-flto -LDLIBS= -lpthread -static-libgcc -static-libstdc++ +LDLIBS= -lpthread RELEASE_FLAGS=-O2 DEBUG_FLAGS=-O0 -ggdb -fsanitize=undefined -DDebug @@ -16,7 +16,7 @@ LDLIBS:=-lwinmm $(LDLIBS) else CC=gcc CXX=g++ -CPPFLAGS:=$(CPPFLAGS) -D __LINUX_ALSA__ -LDLIBS:=-lasound $(LDLIBS) +CPPFLAGS:=$(CPPFLAGS) -D __MACOSX_CORE__ +LDLIBS:=-framework CoreMIDI -framework CoreAudio -framework CoreFoundation $(LDLIBS) endif diff --git a/musique/algo.hh b/musique/algo.hh index b592c46..e1785a9 100644 --- a/musique/algo.hh +++ b/musique/algo.hh @@ -5,13 +5,14 @@ #include #include #include +#include /// Generic algorithms support namespace algo { /// Check if predicate is true for all successive pairs of elements constexpr bool pairwise_all( - std::ranges::forward_range auto &&range, + auto &&range, auto &&binary_predicate) { auto it = std::begin(range); @@ -34,6 +35,22 @@ namespace algo } return init; } + + /// Equavilent of std::lexicographical_compare_three_way + /// + /// It's missing from MacOS C++ standard library + constexpr auto lexicographical_compare(auto&& lhs_range, auto&& rhs_range) + { + auto lhs = lhs_range.begin(); + auto rhs = rhs_range.begin(); + decltype(*lhs <=> *rhs) result = std::partial_ordering::equivalent; + for (; lhs != lhs_range.end() && rhs != rhs_range.end(); ++lhs, ++rhs) { + if ((result = *lhs <=> *rhs) != 0) { + return result; + } + } + return result; + } } /// Flattens one layer: `[[[1], 2], 3]` becomes `[[1], 2, 3]` diff --git a/musique/common.hh b/musique/common.hh index 800b12b..5cc92b9 100644 --- a/musique/common.hh +++ b/musique/common.hh @@ -54,4 +54,9 @@ constexpr std::size_t hash_combine(std::size_t lhs, std::size_t rhs) { template static constexpr bool always_false = false; +template +concept Three_Way_Comparable = requires (T const& lhs, T const& rhs) { + { lhs <=> rhs }; +}; + #endif diff --git a/musique/errors.cc b/musique/errors.cc index 0fabeee..39a7e10 100644 --- a/musique/errors.cc +++ b/musique/errors.cc @@ -81,7 +81,7 @@ static void encourage_contact(std::ostream &os) << pretty::end << std::flush; } -void assert(bool condition, std::string message, Location loc) +void ensure(bool condition, std::string message, Location loc) { if (condition) return; #if Debug diff --git a/musique/errors.hh b/musique/errors.hh index 9036e55..769345d 100644 --- a/musique/errors.hh +++ b/musique/errors.hh @@ -15,13 +15,8 @@ #include #include -// To make sure, that we don't collide with macro -#ifdef assert -#undef assert -#endif - /// Guards that program exits if condition does not hold -void assert(bool condition, std::string message, Location loc = Location::caller()); +void ensure(bool condition, std::string message, Location loc = Location::caller()); /// Marks part of code that was not implemented yet [[noreturn]] void unimplemented(std::string_view message = {}, Location loc = Location::caller()); diff --git a/musique/format.cc b/musique/format.cc index e91f7b1..b721106 100644 --- a/musique/format.cc +++ b/musique/format.cc @@ -1,8 +1,8 @@ +#include #include #include #include #include -#include Result format(Interpreter &i, Value const& value) { diff --git a/musique/interpreter/builtin_functions.cc b/musique/interpreter/builtin_functions.cc index 9e17c1c..ab0e255 100644 --- a/musique/interpreter/builtin_functions.cc +++ b/musique/interpreter/builtin_functions.cc @@ -14,7 +14,7 @@ void Interpreter::register_callbacks() { - assert(callbacks == nullptr, "This field should be uninitialized"); + ensure(callbacks == nullptr, "This field should be uninitialized"); callbacks = std::make_unique(); callbacks->add_callbacks(*midi_connection, *this); } @@ -67,8 +67,8 @@ static inline std::optional create_chord(std::vector &chord, Interp } if (auto arg_chord = get_if(arg)) { - std::ranges::copy_if( - arg_chord->notes, + std::copy_if( + arg_chord->notes.begin(), arg_chord->notes.end(), std::back_inserter(chord), [](Note const& n) { return n.base.has_value(); } ); @@ -80,7 +80,7 @@ static inline std::optional create_chord(std::vector &chord, Interp continue; } - assert(false, "this type is not supported inside chord"); // TODO(assert) + ensure(false, "this type is not supported inside chord"); // TODO(assert) } return {}; @@ -90,7 +90,7 @@ static inline std::optional create_chord(std::vector &chord, Interp template static 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) + ensure(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))>; @@ -98,7 +98,7 @@ static Result ctx_read_write_property(Interpreter &interpreter, std::vect return Number(interpreter.context_stack.back().*(Mem_Ptr)); } - assert(std::holds_alternative(args.front().data), "Ctx only holds numeric values"); + ensure(std::holds_alternative(args.front().data), "Ctx only holds numeric values"); if constexpr (std::is_same_v) { interpreter.context_stack.back().*(Mem_Ptr) = std::get(args.front().data); @@ -118,7 +118,7 @@ static Result into_flat_array(Interpreter &interpreter, std::span for (auto &arg : args) { std::visit(Overloaded { [&target](Array &&array) -> std::optional { - std::ranges::move(array.elements, std::back_inserter(target.elements)); + std::move(array.elements.begin(), array.elements.end(), std::back_inserter(target.elements)); return {}; }, [&target, &interpreter](Block &&block) -> std::optional { @@ -300,10 +300,10 @@ static inline Result builtin_play(Interpreter &i, Container args) static Result builtin_par(Interpreter &i, std::vector args) { Try(ensure_midi_connection_available(i, Midi_Connection_Type::Output, "par")); - assert(args.size() >= 1, "par only makes sense for at least one argument"); // TODO(assert) + ensure(args.size() >= 1, "par only makes sense for at least one argument"); // TODO(assert) if (args.size() == 1) { auto chord = get_if(args.front()); - assert(chord, "Par expects music value as first argument"); // TODO(assert) + ensure(chord, "Par expects music value as first argument"); // TODO(assert) Try(i.play(std::move(*chord))); return Value{}; } @@ -311,7 +311,7 @@ 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 = get_if(args.front()); - assert(chord, "par expects music value as first argument"); // TODO(assert) + ensure(chord, "par expects music value as first argument"); // TODO(assert) std::for_each(chord->notes.begin(), chord->notes.end(), [&](Note ¬e) { note = ctx.fill(note); }); @@ -674,7 +674,7 @@ static Result builtin_update(Interpreter &i, std::vector args) /// Return typeof variable static Result builtin_typeof(Interpreter&, std::vector args) { - assert(args.size() == 1, "typeof expects only one argument"); // TODO(assert) + ensure(args.size() == 1, "typeof expects only one argument"); // TODO(assert) return Symbol(type_name(args.front())); } @@ -701,7 +701,7 @@ static Result builtin_shuffle(Interpreter &i, std::vector args) { static std::mt19937 rnd{std::random_device{}()}; auto array = Try(flatten(i, std::move(args))); - std::ranges::shuffle(array, rnd); + std::shuffle(array.begin(), array.end(), rnd); return array; } @@ -709,7 +709,7 @@ static Result builtin_shuffle(Interpreter &i, std::vector args) static Result builtin_permute(Interpreter &i, std::vector args) { auto array = Try(flatten(i, std::move(args))); - std::ranges::next_permutation(array); + std::next_permutation(array.begin(), array.end()); return array; } @@ -717,7 +717,7 @@ static Result builtin_permute(Interpreter &i, std::vector args) static Result builtin_sort(Interpreter &i, std::vector args) { auto array = Try(flatten(i, std::move(args))); - std::ranges::sort(array); + std::sort(array.begin(), array.end()); return array; } @@ -725,7 +725,7 @@ static Result builtin_sort(Interpreter &i, std::vector args) static Result builtin_reverse(Interpreter &i, std::vector args) { auto array = Try(flatten(i, std::move(args))); - std::ranges::reverse(array); + std::reverse(array.begin(), array.end()); return array; } @@ -733,7 +733,7 @@ static Result builtin_reverse(Interpreter &i, std::vector args) static Result builtin_min(Interpreter &i, std::vector args) { auto array = Try(deep_flat(i, args)); - if (auto min = std::ranges::min_element(array); min != array.end()) + if (auto min = std::min_element(array.begin(), array.end()); min != array.end()) return *min; return Value{}; } @@ -742,7 +742,7 @@ static Result builtin_min(Interpreter &i, std::vector args) static Result builtin_max(Interpreter &i, std::vector args) { auto array = Try(deep_flat(i, args)); - if (auto max = std::ranges::max_element(array); max != array.end()) + if (auto max = std::max_element(array.begin(), array.end()); max != array.end()) return *max; return Value{}; } diff --git a/musique/interpreter/builtin_operators.cc b/musique/interpreter/builtin_operators.cc index d8f7d9f..38306db 100644 --- a/musique/interpreter/builtin_operators.cc +++ b/musique/interpreter/builtin_operators.cc @@ -5,6 +5,8 @@ #include #include +#include + /// Intrinsic implementation primitive to ease operation vectorization static Result vectorize(auto &&operation, Interpreter &interpreter, Value lhs, Value rhs) { @@ -20,7 +22,7 @@ static Result vectorize(auto &&operation, Interpreter &interpreter, Value return array; } - assert(rhs_coll != nullptr, "Trying to vectorize two non-collections"); + ensure(rhs_coll != nullptr, "Trying to vectorize two non-collections"); Array array; for (auto i = 0u; i < rhs_coll->size(); ++i) { @@ -34,7 +36,7 @@ static Result vectorize(auto &&operation, Interpreter &interpreter, Value /// @invariant args.size() == 2 static Result vectorize(auto &&operation, Interpreter &interpreter, std::vector args) { - assert(args.size() == 2, "Vectorization primitive only supports two arguments"); + ensure(args.size() == 2, "Vectorization primitive only supports two arguments"); return vectorize(std::move(operation), interpreter, std::move(args.front()), std::move(args.back())); } diff --git a/musique/interpreter/env.cc b/musique/interpreter/env.cc index ba1cc23..9bd749d 100644 --- a/musique/interpreter/env.cc +++ b/musique/interpreter/env.cc @@ -7,7 +7,7 @@ std::shared_ptr Env::global = nullptr; std::shared_ptr Env::make() { auto new_env = new Env(); - assert(new_env, "Cannot construct new env"); + ensure(new_env, "Cannot construct new env"); return std::shared_ptr(new_env); } diff --git a/musique/interpreter/interpreter.cc b/musique/interpreter/interpreter.cc index 9534e45..1132676 100644 --- a/musique/interpreter/interpreter.cc +++ b/musique/interpreter/interpreter.cc @@ -35,7 +35,7 @@ Interpreter::Interpreter() context_stack.emplace_back(); // Environment initlialization - assert(!bool(Env::global), "Only one instance of interpreter can be at one time"); + ensure(!bool(Env::global), "Only one instance of interpreter can be at one time"); env = Env::global = Env::make(); // Builtins initialization @@ -83,16 +83,16 @@ Result Interpreter::eval(Ast &&ast) case Ast::Type::Binary: { - assert(ast.arguments.size() == 2, "Expected arguments of binary operation to be 2 long"); + ensure(ast.arguments.size() == 2, "Expected arguments of binary operation to be 2 long"); if (ast.token.source == "=") { auto lhs = std::move(ast.arguments.front()); auto rhs = std::move(ast.arguments.back()); - assert(lhs.type == Ast::Type::Literal && lhs.token.type == Token::Type::Symbol, + ensure(lhs.type == Ast::Type::Literal && lhs.token.type == Token::Type::Symbol, "Currently LHS of assigment must be an identifier"); // TODO(assert) Value *v = env->find(std::string(lhs.token.source)); - assert(v, "Cannot resolve variable: "s + std::string(lhs.token.source)); // TODO(assert) + ensure(v, "Cannot resolve variable: "s + std::string(lhs.token.source)); // TODO(assert) return *v = Try(eval(std::move(rhs)).with_location(ast.token.location)); } @@ -124,11 +124,11 @@ Result Interpreter::eval(Ast &&ast) auto lhs = std::move(ast.arguments.front()); auto rhs = std::move(ast.arguments.back()); auto const rhs_loc = rhs.location; - assert(lhs.type == Ast::Type::Literal && lhs.token.type == Token::Type::Symbol, + ensure(lhs.type == Ast::Type::Literal && lhs.token.type == Token::Type::Symbol, "Currently LHS of assigment must be an identifier"); // TODO(assert) Value *v = env->find(std::string(lhs.token.source)); - assert(v, "Cannot resolve variable: "s + std::string(lhs.token.source)); // TODO(assert) + ensure(v, "Cannot resolve variable: "s + std::string(lhs.token.source)); // TODO(assert) return *v = Try(op->second(*this, { *v, Try(eval(std::move(rhs)).with_location(rhs_loc)) }).with_location(ast.token.location)); @@ -179,9 +179,9 @@ Result Interpreter::eval(Ast &&ast) case Ast::Type::Variable_Declaration: { - assert(ast.arguments.size() == 2, "Only simple assigments are supported now"); - assert(ast.arguments.front().type == Ast::Type::Literal, "Only names are supported as LHS arguments now"); - assert(ast.arguments.front().token.type == Token::Type::Symbol, "Only names are supported as LHS arguments now"); + ensure(ast.arguments.size() == 2, "Only simple assigments are supported now"); + ensure(ast.arguments.front().type == Ast::Type::Literal, "Only names are supported as LHS arguments now"); + ensure(ast.arguments.front().token.type == Token::Type::Symbol, "Only names are supported as LHS arguments now"); env->force_define(std::string(ast.arguments.front().token.source), Try(eval(std::move(ast.arguments.back())))); return Value{}; } @@ -191,10 +191,10 @@ Result Interpreter::eval(Ast &&ast) { Block block; if (ast.type == Ast::Type::Lambda) { - auto parameters = std::span(ast.arguments.begin(), std::prev(ast.arguments.end())); + auto parameters = std::span(ast.arguments.data(), ast.arguments.size() - 1); block.parameters.reserve(parameters.size()); for (auto ¶m : parameters) { - assert(param.type == Ast::Type::Literal && param.token.type == Token::Type::Symbol, "Not a name in parameter section of Ast::lambda"); + ensure(param.type == Ast::Type::Literal && param.token.type == Token::Type::Symbol, "Not a name in parameter section of Ast::lambda"); block.parameters.push_back(std::string(std::move(param).token.source)); } } @@ -217,7 +217,7 @@ void Interpreter::enter_scope() void Interpreter::leave_scope() { - assert(env != Env::global, "Cannot leave global scope"); + ensure(env != Env::global, "Cannot leave global scope"); env = env->leave(); } diff --git a/musique/interpreter/interpreter.hh b/musique/interpreter/interpreter.hh index 461e722..83cb729 100644 --- a/musique/interpreter/interpreter.hh +++ b/musique/interpreter/interpreter.hh @@ -4,6 +4,7 @@ #include #include #include +#include /// Given program tree evaluates it into Value struct Interpreter diff --git a/musique/lexer/lexer.cc b/musique/lexer/lexer.cc index 079b3f3..fa8ca08 100644 --- a/musique/lexer/lexer.cc +++ b/musique/lexer/lexer.cc @@ -226,7 +226,7 @@ auto Lexer::consume_if(auto first, auto second) -> bool void Lexer::rewind() { - assert(last_rune_length != 0, "cannot rewind to not existing rune"); + ensure(last_rune_length != 0, "cannot rewind to not existing rune"); source = { source.data() - last_rune_length, source.size() + last_rune_length }; token_length -= last_rune_length; location = prev_location; diff --git a/musique/lexer/lines.cc b/musique/lexer/lines.cc index 114e17c..7be61c6 100644 --- a/musique/lexer/lines.cc +++ b/musique/lexer/lines.cc @@ -23,7 +23,7 @@ void Lines::add_file(std::string filename, std::string_view source) void Lines::add_line(std::string const& filename, std::string_view source, unsigned line_number) { - assert(line_number != 0, "Line number = 0 is invalid"); + ensure(line_number != 0, "Line number = 0 is invalid"); if (lines[filename].size() <= line_number) lines[filename].resize(line_number); lines[filename][line_number - 1] = source; diff --git a/musique/main.cc b/musique/main.cc index ffe3762..657b1e0 100644 --- a/musique/main.cc +++ b/musique/main.cc @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -21,6 +22,7 @@ extern "C" { #include } #else +#include extern "C" { #include } @@ -126,15 +128,13 @@ struct Runner midi::Rt_Midi midi; Interpreter interpreter; - std::thread midi_input_event_loop; - std::stop_source stop_source; /// Setup interpreter and midi connection with given port Runner(std::optional input_port, std::optional output_port) : midi() , interpreter{} { - assert(the == nullptr, "Only one instance of runner is supported"); + ensure(the == nullptr, "Only one instance of runner is supported"); the = this; bool const midi_go = bool(input_port) || bool(output_port); @@ -149,11 +149,6 @@ struct Runner std::cout << "Connected MIDI input to port " << *input_port << ". Ready for incoming messages!" << std::endl; midi.connect_input(*input_port); } - if (midi_go) { - interpreter.register_callbacks(); - midi_input_event_loop = std::thread([this] { handle_midi_event_loop(); }); - midi_input_event_loop.detach(); - } Env::global->force_define("say", +[](Interpreter &interpreter, std::vector args) -> Result { for (auto it = args.begin(); it != args.end(); ++it) { @@ -166,21 +161,11 @@ struct Runner }); } - ~Runner() - { - stop_source.request_stop(); - } - Runner(Runner const&) = delete; Runner(Runner &&) = delete; Runner& operator=(Runner const&) = delete; Runner& operator=(Runner &&) = delete; - void handle_midi_event_loop() - { - midi.input_event_loop(stop_source.get_token()); - } - /// Run given source std::optional run(std::string_view source, std::string_view filename, bool output = false) { @@ -221,7 +206,7 @@ bool is_tty() #ifdef _WIN32 return _isatty(STDOUT_FILENO); #else - return isatty(STDOUT_FILENO); + return isatty(fileno(stdout)); #endif } diff --git a/musique/midi/midi.hh b/musique/midi/midi.hh index 02a9db5..ac3ee18 100644 --- a/musique/midi/midi.hh +++ b/musique/midi/midi.hh @@ -3,7 +3,6 @@ #include #include #include -#include #include // Documentation of midi messages available at http://midi.teragonaudio.com/tech/midispec.htm @@ -48,8 +47,6 @@ namespace midi void send_program_change(uint8_t channel, uint8_t program) override; void send_controller_change(uint8_t channel, uint8_t controller_number, uint8_t value) override; - void input_event_loop(std::stop_token); - std::optional input; std::optional output; }; diff --git a/musique/midi/rt_midi.cc b/musique/midi/rt_midi.cc index ac9adb5..a8da393 100644 --- a/musique/midi/rt_midi.cc +++ b/musique/midi/rt_midi.cc @@ -36,9 +36,11 @@ try { void midi::Rt_Midi::connect_output(unsigned target) try { - assert(not output.has_value(), "Reconeccting is not supported yet"); + ensure(not output.has_value(), "Reconeccting is not supported yet"); output.emplace(); - output->openPort(target, "Musique output port"); + output->openVirtualPort("Musique output port"); + + // output->openPort(target, "Musique output port"); } catch (RtMidiError &error) { // TODO(error) std::cerr << "Failed to use MIDI connection: " << error.getMessage() << std::endl; @@ -47,7 +49,7 @@ try { void midi::Rt_Midi::connect_input(unsigned target) try { - assert(not input.has_value(), "Reconeccting is not supported yet"); + ensure(not input.has_value(), "Reconeccting is not supported yet"); input.emplace(); input->openPort(target, "Musique input port"); } catch (RtMidiError &error) { @@ -104,6 +106,3 @@ void midi::Rt_Midi::send_controller_change(uint8_t channel, uint8_t controller_n send_message(*output, std::array { std::uint8_t(Control_Change + channel), controller_number, value }); } -void midi::Rt_Midi::input_event_loop(std::stop_token) -{ -} diff --git a/musique/parser/parser.cc b/musique/parser/parser.cc index fff86a0..3398280 100644 --- a/musique/parser/parser.cc +++ b/musique/parser/parser.cc @@ -118,7 +118,7 @@ Result Parser::parse_variable_declaration() return std::move(lvalue).error(); } - assert(expect(Token::Type::Operator, ":="), "This function should always be called with valid sequence"); + ensure(expect(Token::Type::Operator, ":="), "This function should always be called with valid sequence"); consume(); return Ast::variable_declaration(lvalue->location, { *std::move(lvalue) }, Try(parse_expression())); } @@ -133,7 +133,7 @@ Result Parser::parse_infix_expression() || expect(Token::Type::Keyword, "or"); if (next_is_operator) { - assert(not expect(Token::Type::Operator, "."), "This should be handled by parse_index_expression"); + ensure(not expect(Token::Type::Operator, "."), "This should be handled by parse_index_expression"); auto op = consume(); Ast ast; @@ -161,7 +161,7 @@ Result Parser::parse_rhs_of_infix_expression(Ast lhs) return lhs; } - assert(not expect(Token::Type::Operator, "."), "This should be handled by parse_index_expression"); + ensure(not expect(Token::Type::Operator, "."), "This should be handled by parse_index_expression"); auto op = consume(); if (precedense(lhs.token.source) >= precedense(op.source)) { @@ -249,7 +249,7 @@ Result Parser::parse_atomic_expression() if (not expect(Token::Type::Close_Block)) { if (expect(Token::Type::Parameter_Separator)) { if (is_lambda) { - assert(false, "There should be error message that you cannot put multiple parameter separators in one block"); + ensure(false, "There should be error message that you cannot put multiple parameter separators in one block"); } else { // This may be a result of user trying to specify parameters from things that cannot be parameters (like chord literals) // or accidential hit of "|" on the keyboard. We can detect first case by ensuring that all tokens between this place @@ -467,7 +467,7 @@ Ast Ast::binary(Token token, Ast lhs, Ast rhs) Ast Ast::call(std::vector call) { - assert(!call.empty(), "Call must have at least pice of code that is beeing called"); + ensure(!call.empty(), "Call must have at least pice of code that is beeing called"); Ast ast; ast.type = Type::Call; diff --git a/musique/try.hh b/musique/try.hh index 4b31e20..6163e4a 100644 --- a/musique/try.hh +++ b/musique/try.hh @@ -46,13 +46,13 @@ struct Try_Traits> static std::nullopt_t yield_value(std::optional&& err) { - assert(not err.has_value(), "Trying to yield value from optional that contains error"); + ensure(not err.has_value(), "Trying to yield value from optional that contains error"); return std::nullopt; } static Error yield_error(std::optional&& err) { - assert(err.has_value(), "Trying to yield value from optional that NOT constains error"); + ensure(err.has_value(), "Trying to yield value from optional that NOT constains error"); return std::move(*err); } }; @@ -70,7 +70,7 @@ struct Try_Traits> static auto yield_value(Result val) { - assert(val.has_value(), "Trying to yield value from expected that contains error"); + ensure(val.has_value(), "Trying to yield value from expected that contains error"); if constexpr (std::is_void_v) { } else { return std::move(*val); @@ -79,7 +79,7 @@ struct Try_Traits> static Error yield_error(Result&& val) { - assert(not val.has_value(), "Trying to yield error from expected with value"); + ensure(not val.has_value(), "Trying to yield error from expected with value"); return std::move(val.error()); } }; diff --git a/musique/value/block.cc b/musique/value/block.cc index db9fb34..f8128e3 100644 --- a/musique/value/block.cc +++ b/musique/value/block.cc @@ -16,7 +16,7 @@ static inline std::optional guard_index(unsigned index, unsigned size) // TODO Add memoization Result Block::index(Interpreter &i, unsigned position) const { - assert(parameters.size() == 0, "cannot index into block with parameters (for now)"); + ensure(parameters.size() == 0, "cannot index into block with parameters (for now)"); if (body.type != Ast::Type::Sequence) { Try(guard_index(position, 1)); return i.eval((Ast)body); diff --git a/musique/value/chord.cc b/musique/value/chord.cc index abdcc64..7ff6884 100644 --- a/musique/value/chord.cc +++ b/musique/value/chord.cc @@ -17,7 +17,7 @@ Chord::Chord(std::vector &¬es) Chord Chord::from(std::string_view source) { auto note = Note::from(source); - assert(note.has_value(), "don't know how this could happen"); + ensure(note.has_value(), "don't know how this could happen"); Chord chord; source.remove_prefix(1 + (source[1] == '#')); @@ -120,7 +120,7 @@ Result Chord::operator()(Interpreter& interpreter, std::vector arg std::move(current.begin(), current.end(), std::back_inserter(array)); - assert(not array.empty(), "At least *this should be in this array"); + ensure(not array.empty(), "At least *this should be in this array"); return array; } diff --git a/musique/value/function.hh b/musique/value/function.hh index 45e244c..3190eb9 100644 --- a/musique/value/function.hh +++ b/musique/value/function.hh @@ -12,6 +12,7 @@ struct Function virtual Result operator()(Interpreter &i, std::vector params) const = 0; constexpr bool operator==(Function const&) const = default; + constexpr std::strong_ordering operator<=>(Function const&) const = default; }; #endif // MUSIQUE_VALUE_FUNCTION_HH diff --git a/musique/value/intrinsic.hh b/musique/value/intrinsic.hh index a91ad58..a8f0b4d 100644 --- a/musique/value/intrinsic.hh +++ b/musique/value/intrinsic.hh @@ -26,6 +26,8 @@ struct Intrinsic : Function /// Compares if function pointers are equal bool operator==(Intrinsic const&) const = default; + + std::strong_ordering operator<=>(Intrinsic const& rhs) const = default; }; #endif // MUSIQUE_VALUE_INTRINSIC_HH diff --git a/musique/value/note.cc b/musique/value/note.cc index 36fe2b3..867a649 100644 --- a/musique/value/note.cc +++ b/musique/value/note.cc @@ -70,7 +70,7 @@ std::optional Note::into_midi_note() const u8 Note::into_midi_note(i8 default_octave) const { - assert(bool(this->base), "Pause don't translate into MIDI"); + ensure(bool(this->base), "Pause don't translate into MIDI"); auto const octave = this->octave.has_value() ? *this->octave : default_octave; // octave is in range [-1, 9] where Note { .base = 0, .octave = -1 } is midi note 0 return (octave + 1) * 12 + *base; diff --git a/musique/value/number.cc b/musique/value/number.cc index c3efd47..3a5f505 100644 --- a/musique/value/number.cc +++ b/musique/value/number.cc @@ -19,7 +19,7 @@ auto Number::as_int() const -> i64 { // We don't perform GCD simplification in place due to constness auto self = simplify(); - assert(self.den == 1 || self.num == 0, "Implicit coarce to integer while holding fractional number is not allowed"); + ensure(self.den == 1 || self.num == 0, "Implicit coarce to integer while holding fractional number is not allowed"); return self.num; } @@ -203,7 +203,7 @@ static consteval auto compute_powers(Number::value_type multiplier) -> std::arra static Number::value_type pow10(usize n) { static constexpr auto Powers = compute_powers(10); - assert(n < Powers.size(), "Trying to compute power of 10 grater then current type can hold"); + ensure(n < Powers.size(), "Trying to compute power of 10 grater then current type can hold"); return Powers[n]; } diff --git a/musique/value/value.cc b/musique/value/value.cc index 355bccd..5400413 100644 --- a/musique/value/value.cc +++ b/musique/value/value.cc @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -7,9 +8,25 @@ #include #include +#include Value::Value() = default; +std::strong_ordering operator<=>(std::string const& lhs, std::string const& rhs) +{ + if (auto cmp = lhs.size() <=> rhs.size(); cmp == 0) { + if (auto cmp = strncmp(lhs.c_str(), rhs.c_str(), lhs.size()); cmp == 0) { + return std::strong_ordering::equal; + } else if (cmp < 0) { + return std::strong_ordering::less; + } else { + return std::strong_ordering::greater; + } + } else { + return cmp; + } +} + Result Value::from(Token t) { switch (t.type) { @@ -28,7 +45,7 @@ Result Value::from(Token t) case Token::Type::Chord: if (t.source.size() == 1 || (t.source.size() == 2 && t.source.back() == '#')) { auto maybe_note = Note::from(t.source); - assert(maybe_note.has_value(), "Somehow parser passed invalid note literal"); + ensure(maybe_note.has_value(), "Somehow parser passed invalid note literal"); return *maybe_note; } @@ -104,7 +121,7 @@ Result Value::index(Interpreter &i, unsigned position) const if (auto collection = get_if(data)) { return collection->index(i, position); } - assert(false, "Block indexing is not supported for this type"); // TODO(assert) + ensure(false, "Block indexing is not supported for this type"); // TODO(assert) unreachable(); } @@ -141,7 +158,7 @@ usize Value::size() const return collection->size(); } - assert(false, "This type does not support Value::size()"); // TODO(assert) + ensure(false, "This type does not support Value::size()"); // TODO(assert) unreachable(); } @@ -151,18 +168,12 @@ std::partial_ordering Value::operator<=>(Value const& rhs) const return std::visit(Overloaded { [](Nil, Nil) { return std::partial_ordering::equivalent; }, [](Array const& lhs, Array const& rhs) { - return std::lexicographical_compare_three_way( - lhs.elements.begin(), lhs.elements.end(), - rhs.elements.begin(), rhs.elements.end() - ); + return algo::lexicographical_compare(lhs.elements, rhs.elements); }, [](Chord const& lhs, Chord const& rhs) { - return std::lexicographical_compare_three_way( - lhs.notes.begin(), lhs.notes.end(), - rhs.notes.begin(), rhs.notes.end() - ); + return algo::lexicographical_compare(lhs.notes, rhs.notes); }, - [](T const& lhs, T const& rhs) -> std::partial_ordering requires std::three_way_comparable { + [](T const& lhs, T const& rhs) -> std::partial_ordering requires Three_Way_Comparable { return lhs <=> rhs; }, [](auto&&...) { return std::partial_ordering::unordered; } From 0d7f4b1eb5a3d4f1f0b8ac2f8a8763796810321e Mon Sep 17 00:00:00 2001 From: Robert Bendun Date: Fri, 14 Oct 2022 15:56:36 +0200 Subject: [PATCH 2/4] Build system supports both MacOS and Linux now --- Makefile | 8 ++++---- config.mk | 12 +++--------- musique/value/value.cc | 3 ++- scripts/linux.mk | 5 +++++ scripts/macos.mk | 7 +++++++ scripts/release.mk | 12 +++++------- scripts/windows.mk | 14 ++++---------- 7 files changed, 30 insertions(+), 31 deletions(-) create mode 100644 scripts/linux.mk create mode 100644 scripts/macos.mk diff --git a/Makefile b/Makefile index 7744d55..ddedcf7 100644 --- a/Makefile +++ b/Makefile @@ -5,13 +5,13 @@ Obj := $(subst musique/,,$(Sources:%.cc=%.o)) all: bin/musique +include scripts/$(os).mk include scripts/debug.mk include scripts/release.mk include scripts/test.mk -include scripts/windows.mk # http://www.music.mcgill.ca/~gary/rtmidi/#compiling -bin/rtmidi.o: lib/rtmidi/RtMidi.cpp lib/rtmidi/RtMidi.h +bin/$(os)/rtmidi.o: lib/rtmidi/RtMidi.cpp lib/rtmidi/RtMidi.h @echo "CXX $@" @$(CXX) $< -c -O2 -o $@ $(CPPFLAGS) -std=c++20 @@ -32,5 +32,5 @@ install: bin/musique .PHONY: clean doc doc-open all test unit-tests release install -$(shell mkdir -p $(subst musique/,bin/,$(shell find musique/* -type d))) -$(shell mkdir -p $(subst musique/,bin/debug/,$(shell find musique/* -type d))) +$(shell mkdir -p $(subst musique/,bin/$(os)/,$(shell find musique/* -type d))) +$(shell mkdir -p $(subst musique/,bin/$(os)/debug/,$(shell find musique/* -type d))) diff --git a/config.mk b/config.mk index 4bb0e4a..dd14879 100644 --- a/config.mk +++ b/config.mk @@ -8,15 +8,9 @@ LDLIBS= -lpthread RELEASE_FLAGS=-O2 DEBUG_FLAGS=-O0 -ggdb -fsanitize=undefined -DDebug -ifeq ($(os),windows) -CC=i686-w64-mingw32-gcc -CXX=i686-w64-mingw32-g++ -CPPFLAGS:=$(CPPFLAGS) -D__WINDOWS_MM__ -LDLIBS:=-lwinmm $(LDLIBS) +ifeq ($(shell uname),Darwin) +os=macos else -CC=gcc -CXX=g++ -CPPFLAGS:=$(CPPFLAGS) -D __MACOSX_CORE__ -LDLIBS:=-framework CoreMIDI -framework CoreAudio -framework CoreFoundation $(LDLIBS) +os=linux endif diff --git a/musique/value/value.cc b/musique/value/value.cc index 5400413..8f12de2 100644 --- a/musique/value/value.cc +++ b/musique/value/value.cc @@ -9,13 +9,14 @@ #include #include #include +#include Value::Value() = default; std::strong_ordering operator<=>(std::string const& lhs, std::string const& rhs) { if (auto cmp = lhs.size() <=> rhs.size(); cmp == 0) { - if (auto cmp = strncmp(lhs.c_str(), rhs.c_str(), lhs.size()); cmp == 0) { + if (auto cmp = std::strncmp(lhs.c_str(), rhs.c_str(), lhs.size()); cmp == 0) { return std::strong_ordering::equal; } else if (cmp < 0) { return std::strong_ordering::less; diff --git a/scripts/linux.mk b/scripts/linux.mk new file mode 100644 index 0000000..6a611ac --- /dev/null +++ b/scripts/linux.mk @@ -0,0 +1,5 @@ +CC=gcc +CXX=g++ +CPPFLAGS:=$(CPPFLAGS) -D __LINUX_ALSA__ +LDLIBS:=-lasound $(LDLIBS) -static-libgcc -static-libstdc++ +Bestline=bin/$(os)/bestline.o diff --git a/scripts/macos.mk b/scripts/macos.mk new file mode 100644 index 0000000..a961237 --- /dev/null +++ b/scripts/macos.mk @@ -0,0 +1,7 @@ +CC=clang +CXX=clang++ +CPPFLAGS:=$(CPPFLAGS) -D __MACOSX_CORE__ +LDLIBS:=-framework CoreMIDI -framework CoreAudio -framework CoreFoundation $(LDLIBS) +Release_Obj=$(addprefix bin/,$(Obj)) +Bestline=bin/$(os)/bestline.o + diff --git a/scripts/release.mk b/scripts/release.mk index 01d5eb6..7403e69 100644 --- a/scripts/release.mk +++ b/scripts/release.mk @@ -1,15 +1,13 @@ -ifneq ($(os),windows) -Release_Obj=$(addprefix bin/,$(Obj)) +Release_Obj=$(addprefix bin/$(os)/,$(Obj)) -bin/bestline.o: lib/bestline/bestline.c lib/bestline/bestline.h +bin/$(os)/bestline.o: lib/bestline/bestline.c lib/bestline/bestline.h @echo "CC $@" @$(CC) $< -c -O3 -o $@ -bin/%.o: musique/%.cc +bin/$(os)/%.o: musique/%.cc @echo "CXX $@" @$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) $(CPPFLAGS) -o $@ $< -c -bin/musique: $(Release_Obj) bin/main.o bin/rtmidi.o bin/bestline.o +bin/musique: $(Release_Obj) bin/$(os)/main.o bin/$(os)/rtmidi.o $(Bestline) @echo "CXX $@" - @$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) $(CPPFLAGS) -o $@ $(Release_Obj) bin/rtmidi.o bin/bestline.o $(LDFLAGS) $(LDLIBS) -endif + @$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) $(CPPFLAGS) -o $@ $(Release_Obj) bin/$(os)/rtmidi.o $(Bestline) $(LDFLAGS) $(LDLIBS) diff --git a/scripts/windows.mk b/scripts/windows.mk index d312494..db55123 100644 --- a/scripts/windows.mk +++ b/scripts/windows.mk @@ -1,11 +1,5 @@ -ifeq ($(os),windows) -Release_Obj=$(addprefix bin/,$(Obj)) +CC=i686-w64-mingw32-gcc +CXX=i686-w64-mingw32-g++ +CPPFLAGS:=$(CPPFLAGS) -D__WINDOWS_MM__ +LDLIBS:=-lwinmm $(LDLIBS) -static-libgcc -static-libstdc++ -bin/%.o: musique/%.cc - @echo "CXX $@" - @$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) $(CPPFLAGS) -o $@ $< -c - -bin/musique: $(Release_Obj) bin/main.o bin/rtmidi.o - @echo "CXX $@" - @$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) $(CPPFLAGS) -o $@ $(Release_Obj) bin/rtmidi.o $(LDFLAGS) $(LDLIBS) -endif From e9d67178dabd728961570bec1d81cd5bb9b5ecf3 Mon Sep 17 00:00:00 2001 From: Robert Bendun Date: Fri, 14 Oct 2022 16:24:42 +0200 Subject: [PATCH 3/4] Added virtual port creation as default action. Restored port connection Removed input ports support --- CHANGELOG.md | 1 + musique/interpreter/builtin_functions.cc | 35 +------------- musique/interpreter/incoming_midi.hh | 60 ------------------------ musique/interpreter/interpreter.cc | 38 ++++----------- musique/interpreter/interpreter.hh | 7 +-- musique/main.cc | 30 +++--------- musique/midi/midi.hh | 12 ++--- musique/midi/rt_midi.cc | 19 +++----- 8 files changed, 30 insertions(+), 172 deletions(-) delete mode 100644 musique/interpreter/incoming_midi.hh diff --git a/CHANGELOG.md b/CHANGELOG.md index b708a3b..70ab677 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Release package now with compiled Windows binary * `:load` REPL command to load Musique files inside Musique session. Allows for delayed file execution after a connection * `:quit` REPL command that mirrors `:exit` command +* Virtual MIDI output port creation as default action (--output connects to existing one) ### Changed diff --git a/musique/interpreter/builtin_functions.cc b/musique/interpreter/builtin_functions.cc index ab0e255..74f2af2 100644 --- a/musique/interpreter/builtin_functions.cc +++ b/musique/interpreter/builtin_functions.cc @@ -1,7 +1,6 @@ #include #include #include -#include #include #include @@ -12,13 +11,6 @@ #include #include -void Interpreter::register_callbacks() -{ - ensure(callbacks == nullptr, "This field should be uninitialized"); - callbacks = std::make_unique(); - callbacks->add_callbacks(*midi_connection, *this); -} - /// Check if type has index method template concept With_Index_Method = requires (T &t, Interpreter interpreter, usize position) { @@ -276,7 +268,7 @@ static std::optional action_play(Interpreter &i, Value v) template> static inline Result builtin_play(Interpreter &i, Container args) { - Try(ensure_midi_connection_available(i, Midi_Connection_Type::Output, "play")); + Try(ensure_midi_connection_available(i, "play")); auto previous_action = std::exchange(i.default_action, action_play); i.context_stack.push_back(i.context_stack.back()); @@ -298,7 +290,7 @@ static inline Result builtin_play(Interpreter &i, Container args) /// Play first argument while playing all others static Result builtin_par(Interpreter &i, std::vector args) { - Try(ensure_midi_connection_available(i, Midi_Connection_Type::Output, "par")); + Try(ensure_midi_connection_available(i, "par")); ensure(args.size() >= 1, "par only makes sense for at least one argument"); // TODO(assert) if (args.size() == 1) { @@ -911,28 +903,6 @@ static Result builtin_note_off(Interpreter &i, std::vector args) }; } -/// Add handler for incoming midi messages -static Result builtin_incoming(Interpreter &i, std::vector args) -{ - if (auto a = match(args)) { - auto& [symbol, fun] = *a; - if (symbol == "note_on" || symbol == "noteon") { - i.callbacks->note_on = std::move(args[1]); - } else if (symbol == "note_off" || symbol == "noteoff") { - i.callbacks->note_off = std::move(args[1]); - } else { - - } - return Value{}; - } - - return errors::Unsupported_Types_For { - .type = errors::Unsupported_Types_For::Function, - .name = "incoming", - .possibilities = { "(symbol, function) -> nil" } - }; -} - /// Interleaves arguments static Result builtin_mix(Interpreter &i, std::vector args) { @@ -999,7 +969,6 @@ void Interpreter::register_builtin_functions() global.force_define("for", builtin_for); global.force_define("hash", builtin_hash); global.force_define("if", builtin_if); - global.force_define("incoming", builtin_incoming); global.force_define("instrument", builtin_program_change); global.force_define("len", builtin_len); global.force_define("max", builtin_max); diff --git a/musique/interpreter/incoming_midi.hh b/musique/interpreter/incoming_midi.hh deleted file mode 100644 index b068f3d..0000000 --- a/musique/interpreter/incoming_midi.hh +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef MUSIQUE_INCOMING_MIDI -#define MUSIQUE_INCOMING_MIDI - -#include - -struct Interpreter::Incoming_Midi_Callbacks -{ - Value note_on{}; - Value note_off{}; - - inline Incoming_Midi_Callbacks() = default; - - Incoming_Midi_Callbacks(Incoming_Midi_Callbacks &&) = delete; - Incoming_Midi_Callbacks(Incoming_Midi_Callbacks const&) = delete; - - Incoming_Midi_Callbacks& operator=(Incoming_Midi_Callbacks &&) = delete; - Incoming_Midi_Callbacks& operator=(Incoming_Midi_Callbacks const&) = delete; - - - inline void add_callbacks(midi::Connection &midi, Interpreter &interpreter) - { - register_callback(midi.note_on_callback, note_on, interpreter); - register_callback(midi.note_off_callback, note_off, interpreter); - } - - template - inline void register_callback(std::function &target, Value &callback, Interpreter &i) - { - if (&callback == ¬e_on || &callback == ¬e_off) { - // This messages have MIDI note number as second value, so they should be represented - // in our own note abstraction, not as numbers. - target = [interpreter = &i, callback = &callback](T ...source_args) - { - if (!std::holds_alternative(callback->data)) { - std::vector args { Number(source_args)... }; - args[1] = Note { - .base = i32(std::get(args[1].data).num % 12), - .octave = std::get(args[1].data).num / 12 - }; - auto result = (*callback)(*interpreter, std::move(args)); - // We discard this since callback is running in another thread. - (void) result; - } - }; - } else { - // Generic case, preserve all passed parameters as numbers - target = [interpreter = &i, callback = &callback](T ...source_args) - { - if (!std::holds_alternative(callback->data)) { - auto result = (*callback)(*interpreter, { Number(source_args)... }); - // We discard this since callback is running in another thread. - (void) result; - } - }; - } - } -}; - - -#endif diff --git a/musique/interpreter/interpreter.cc b/musique/interpreter/interpreter.cc index 1132676..5866b6b 100644 --- a/musique/interpreter/interpreter.cc +++ b/musique/interpreter/interpreter.cc @@ -1,5 +1,4 @@ #include -#include #include #include @@ -223,7 +222,7 @@ void Interpreter::leave_scope() std::optional Interpreter::play(Chord chord) { - Try(ensure_midi_connection_available(*this, Midi_Connection_Type::Output, "play")); + Try(ensure_midi_connection_available(*this, "play")); auto &ctx = context_stack.back(); if (chord.notes.size() == 0) { @@ -260,33 +259,16 @@ std::optional Interpreter::play(Chord chord) return {}; } -std::optional ensure_midi_connection_available(Interpreter &i, Midi_Connection_Type m, std::string_view operation_name) +std::optional ensure_midi_connection_available(Interpreter &i, std::string_view operation_name) { - switch (m) { - break; case Midi_Connection_Type::Output: - if (i.midi_connection == nullptr || !i.midi_connection->supports_output()) { - return Error { - .details = errors::Operation_Requires_Midi_Connection { - .is_input = false, - .name = std::string(operation_name), - }, - .location = {} - }; - } - - break; case Midi_Connection_Type::Input: - if (i.midi_connection == nullptr || !i.midi_connection->supports_input()) { - return Error { - .details = errors::Operation_Requires_Midi_Connection { - .is_input = false, - .name = std::string(operation_name), - }, - .location = {} - }; - } - break; default: - unreachable(); + if (i.midi_connection == nullptr || !i.midi_connection->supports_output()) { + return Error { + .details = errors::Operation_Requires_Midi_Connection { + .is_input = false, + .name = std::string(operation_name), + }, + .location = {} + }; } - return {}; } diff --git a/musique/interpreter/interpreter.hh b/musique/interpreter/interpreter.hh index 83cb729..78bd090 100644 --- a/musique/interpreter/interpreter.hh +++ b/musique/interpreter/interpreter.hh @@ -25,10 +25,6 @@ struct Interpreter std::function(Interpreter&, Value)> default_action; - struct Incoming_Midi_Callbacks; - std::unique_ptr callbacks; - void register_callbacks(); - Interpreter(); ~Interpreter(); Interpreter(Interpreter const&) = delete; @@ -57,7 +53,6 @@ struct Interpreter void register_builtin_operators(); }; -enum class Midi_Connection_Type { Output, Input }; -std::optional ensure_midi_connection_available(Interpreter&, Midi_Connection_Type, std::string_view operation_name); +std::optional ensure_midi_connection_available(Interpreter&, std::string_view operation_name); #endif diff --git a/musique/main.cc b/musique/main.cc index 657b1e0..f061192 100644 --- a/musique/main.cc +++ b/musique/main.cc @@ -66,8 +66,6 @@ static T pop(std::span &span) "usage: musique [filename]\n" " where filename is path to file with Musique code that will be executed\n" " where options are:\n" - " -i,--input PORT\n" - " provides input port, a place where Musique receives MIDI messages\n" " -o,--output PORT\n" " provides output port, a place where Musique produces MIDI messages\n" " -l,--list\n" @@ -130,24 +128,20 @@ struct Runner Interpreter interpreter; /// Setup interpreter and midi connection with given port - Runner(std::optional input_port, std::optional output_port) + explicit Runner(std::optional output_port) : midi() , interpreter{} { ensure(the == nullptr, "Only one instance of runner is supported"); the = this; - bool const midi_go = bool(input_port) || bool(output_port); - if (midi_go) { - interpreter.midi_connection = &midi; - } + interpreter.midi_connection = &midi; if (output_port) { - std::cout << "Connected MIDI output to port " << *output_port << ". Ready to play!" << std::endl; midi.connect_output(*output_port); - } - if (input_port) { - std::cout << "Connected MIDI input to port " << *input_port << ". Ready for incoming messages!" << std::endl; - midi.connect_input(*input_port); + std::cout << "Connected MIDI output to port " << *output_port << ". Ready to play!" << std::endl; + } else { + midi.connect_output(); + std::cout << "Created new MIDI output port 'Musique'. Ready to play!" << std::endl; } Env::global->force_define("say", +[](Interpreter &interpreter, std::vector args) -> Result { @@ -297,7 +291,6 @@ static std::optional Main(std::span args) }; // Arbitraly chosen for conviniance of the author - std::optional input_port{}; std::optional output_port{}; std::vector runnables; @@ -334,15 +327,6 @@ static std::optional Main(std::span args) continue; } - if (arg == "-i" || arg == "--input") { - if (args.empty()) { - std::cerr << "musique: error: option " << arg << " requires an argument" << std::endl; - std::exit(1); - } - input_port = pop(args); - continue; - } - if (arg == "-o" || arg == "--output") { if (args.empty()) { std::cerr << "musique: error: option " << arg << " requires an argument" << std::endl; @@ -360,7 +344,7 @@ static std::optional Main(std::span args) std::exit(1); } - Runner runner{input_port, output_port}; + Runner runner{output_port}; for (auto const& [is_file, argument] : runnables) { if (!is_file) { diff --git a/musique/midi/midi.hh b/musique/midi/midi.hh index ac3ee18..bdfc18e 100644 --- a/musique/midi/midi.hh +++ b/musique/midi/midi.hh @@ -13,7 +13,6 @@ namespace midi virtual ~Connection() = default; virtual bool supports_output() const = 0; - virtual bool supports_input () const = 0; virtual void send_note_on (uint8_t channel, uint8_t note_number, uint8_t velocity) = 0; virtual void send_note_off(uint8_t channel, uint8_t note_number, uint8_t velocity) = 0; @@ -21,33 +20,28 @@ namespace midi virtual void send_controller_change(uint8_t channel, uint8_t controller_number, uint8_t value) = 0; void send_all_sounds_off(uint8_t channel); - - std::function note_on_callback = nullptr; - std::function note_off_callback = nullptr; }; struct Rt_Midi : Connection { ~Rt_Midi() override = default; + /// Connect with MIDI virtual port + void connect_output(); + /// Connect with specific MIDI port for outputing MIDI messages void connect_output(unsigned target); - /// Connect with specific MIDI port for reading MIDI messages - void connect_input(unsigned target); - /// List available ports void list_ports(std::ostream &out) const; bool supports_output() const override; - bool supports_input () const override; void send_note_on (uint8_t channel, uint8_t note_number, uint8_t velocity) override; void send_note_off(uint8_t channel, uint8_t note_number, uint8_t velocity) override; void send_program_change(uint8_t channel, uint8_t program) override; void send_controller_change(uint8_t channel, uint8_t controller_number, uint8_t value) override; - std::optional input; std::optional output; }; diff --git a/musique/midi/rt_midi.cc b/musique/midi/rt_midi.cc index a8da393..0307e19 100644 --- a/musique/midi/rt_midi.cc +++ b/musique/midi/rt_midi.cc @@ -34,24 +34,22 @@ try { std::exit(33); } -void midi::Rt_Midi::connect_output(unsigned target) +void midi::Rt_Midi::connect_output() try { ensure(not output.has_value(), "Reconeccting is not supported yet"); output.emplace(); - output->openVirtualPort("Musique output port"); - - // output->openPort(target, "Musique output port"); + output->openVirtualPort("Musique"); } catch (RtMidiError &error) { // TODO(error) std::cerr << "Failed to use MIDI connection: " << error.getMessage() << std::endl; std::exit(33); } -void midi::Rt_Midi::connect_input(unsigned target) +void midi::Rt_Midi::connect_output(unsigned target) try { - ensure(not input.has_value(), "Reconeccting is not supported yet"); - input.emplace(); - input->openPort(target, "Musique input port"); + ensure(not output.has_value(), "Reconeccting is not supported yet"); + output.emplace(); + output->openPort(target); } catch (RtMidiError &error) { // TODO(error) std::cerr << "Failed to use MIDI connection: " << error.getMessage() << std::endl; @@ -63,11 +61,6 @@ bool midi::Rt_Midi::supports_output() const return bool(output); } -bool midi::Rt_Midi::supports_input() const -{ - return bool(input); -} - template inline void send_message(RtMidiOut &out, std::array message) try { From 8348a857f3e155a7ee078d4b589000b0cbc2d623 Mon Sep 17 00:00:00 2001 From: Robert Bendun Date: Fri, 14 Oct 2022 16:43:13 +0200 Subject: [PATCH 4/4] Finalizing build scripts --- CHANGELOG.md | 1 + Makefile | 12 ++++++++++-- scripts/build.mk | 24 ++++++++++++++++++++++++ scripts/debug.mk | 11 ----------- scripts/linux.mk | 1 + scripts/macos.mk | 2 +- scripts/release | 4 ++-- scripts/release.mk | 13 ------------- scripts/windows.mk | 2 +- 9 files changed, 40 insertions(+), 30 deletions(-) create mode 100644 scripts/build.mk delete mode 100644 scripts/debug.mk delete mode 100644 scripts/release.mk diff --git a/CHANGELOG.md b/CHANGELOG.md index 70ab677..0d31c7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added `scan` builtin, which computes prefix sum of passed values when provided with addition operator * Added [rtmidi](https://github.com/thestk/rtmidi/) dependency which should provide multiplatform MIDI support * Support for Windows (with only basic REPL) (`make os=windows`) +* Support for MacOS (`make os=macos`) * Release package now with compiled Windows binary * `:load` REPL command to load Musique files inside Musique session. Allows for delayed file execution after a connection * `:quit` REPL command that mirrors `:exit` command diff --git a/Makefile b/Makefile index ddedcf7..0b9eaa7 100644 --- a/Makefile +++ b/Makefile @@ -3,13 +3,21 @@ include config.mk Sources := $(shell find musique/ -name '*.cc') Obj := $(subst musique/,,$(Sources:%.cc=%.o)) +ifeq ($(os),windows) +all: bin/musique.exe +debug: bin/windows/debug/musique.exe +else all: bin/musique +debug: bin/$(os)/debug/musique +endif include scripts/$(os).mk -include scripts/debug.mk -include scripts/release.mk +include scripts/build.mk include scripts/test.mk +bin/$(Target): bin/$(os)/$(Target) + ln -f $< $@ + # http://www.music.mcgill.ca/~gary/rtmidi/#compiling bin/$(os)/rtmidi.o: lib/rtmidi/RtMidi.cpp lib/rtmidi/RtMidi.h @echo "CXX $@" diff --git a/scripts/build.mk b/scripts/build.mk new file mode 100644 index 0000000..0ef1b20 --- /dev/null +++ b/scripts/build.mk @@ -0,0 +1,24 @@ +Release_Obj=$(addprefix bin/$(os)/,$(Obj)) + +bin/$(os)/bestline.o: lib/bestline/bestline.c lib/bestline/bestline.h + @echo "CC $@" + @$(CC) $< -c -O3 -o $@ + +bin/$(os)/%.o: musique/%.cc + @echo "CXX $@" + @$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) $(CPPFLAGS) -o $@ $< -c + +bin/$(os)/$(Target): $(Release_Obj) bin/$(os)/main.o bin/$(os)/rtmidi.o $(Bestline) + @echo "CXX $@" + @$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) $(CPPFLAGS) -o $@ $(Release_Obj) bin/$(os)/rtmidi.o $(Bestline) $(LDFLAGS) $(LDLIBS) + +Debug_Obj=$(addprefix bin/$(os)/debug/,$(Obj)) + +bin/$(os)/debug/$(Target): $(Debug_Obj) bin/$(os)/debug/main.o bin/$(os)/rtmidi.o $(Bestline) + @echo "CXX $@" + @$(CXX) $(CXXFLAGS) $(DEBUG_FLAGS) $(CPPFLAGS) -o $@ $(Debug_Obj) bin/$(os)/rtmidi.o $(Bestline) $(LDFLAGS) $(LDLIBS) + +bin/$(os)/debug/%.o: musique/%.cc + @echo "CXX $@" + @$(CXX) $(CXXFLAGS) $(DEBUG_FLAGS) $(CPPFLAGS) -o $@ $< -c + diff --git a/scripts/debug.mk b/scripts/debug.mk deleted file mode 100644 index b59424c..0000000 --- a/scripts/debug.mk +++ /dev/null @@ -1,11 +0,0 @@ -Debug_Obj=$(addprefix bin/debug/,$(Obj)) - -debug: bin/debug/musique - -bin/debug/musique: $(Debug_Obj) bin/debug/main.o bin/bestline.o - @echo "CXX $@" - @$(CXX) $(CXXFLAGS) $(DEBUG_FLAGS) $(CPPFLAGS) -o $@ $(Debug_Obj) bin/bestline.o $(LDFLAGS) $(LDLIBS) - -bin/debug/%.o: musique/%.cc - @echo "CXX $@" - @$(CXX) $(CXXFLAGS) $(DEBUG_FLAGS) $(CPPFLAGS) -o $@ $< -c diff --git a/scripts/linux.mk b/scripts/linux.mk index 6a611ac..b0e0e0a 100644 --- a/scripts/linux.mk +++ b/scripts/linux.mk @@ -3,3 +3,4 @@ CXX=g++ CPPFLAGS:=$(CPPFLAGS) -D __LINUX_ALSA__ LDLIBS:=-lasound $(LDLIBS) -static-libgcc -static-libstdc++ Bestline=bin/$(os)/bestline.o +Target=musique diff --git a/scripts/macos.mk b/scripts/macos.mk index a961237..78ed4b6 100644 --- a/scripts/macos.mk +++ b/scripts/macos.mk @@ -4,4 +4,4 @@ CPPFLAGS:=$(CPPFLAGS) -D __MACOSX_CORE__ LDLIBS:=-framework CoreMIDI -framework CoreAudio -framework CoreFoundation $(LDLIBS) Release_Obj=$(addprefix bin/,$(Obj)) Bestline=bin/$(os)/bestline.o - +Target=musique diff --git a/scripts/release b/scripts/release index da4c215..2e7de4b 100755 --- a/scripts/release +++ b/scripts/release @@ -16,10 +16,10 @@ fi mkdir -p "$Target" -make clean && make bin/musique +make clean && make os=linux cp bin/musique "$Target"/ -make clean && make bin/musique os=windows +make clean && make os=windows cp bin/musique.exe "$Target"/ echo "Copy examples, license and documentation" diff --git a/scripts/release.mk b/scripts/release.mk deleted file mode 100644 index 7403e69..0000000 --- a/scripts/release.mk +++ /dev/null @@ -1,13 +0,0 @@ -Release_Obj=$(addprefix bin/$(os)/,$(Obj)) - -bin/$(os)/bestline.o: lib/bestline/bestline.c lib/bestline/bestline.h - @echo "CC $@" - @$(CC) $< -c -O3 -o $@ - -bin/$(os)/%.o: musique/%.cc - @echo "CXX $@" - @$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) $(CPPFLAGS) -o $@ $< -c - -bin/musique: $(Release_Obj) bin/$(os)/main.o bin/$(os)/rtmidi.o $(Bestline) - @echo "CXX $@" - @$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) $(CPPFLAGS) -o $@ $(Release_Obj) bin/$(os)/rtmidi.o $(Bestline) $(LDFLAGS) $(LDLIBS) diff --git a/scripts/windows.mk b/scripts/windows.mk index db55123..a92c6a2 100644 --- a/scripts/windows.mk +++ b/scripts/windows.mk @@ -2,4 +2,4 @@ CC=i686-w64-mingw32-gcc CXX=i686-w64-mingw32-g++ CPPFLAGS:=$(CPPFLAGS) -D__WINDOWS_MM__ LDLIBS:=-lwinmm $(LDLIBS) -static-libgcc -static-libstdc++ - +Target=musique.exe