Halftones moving operator

This commit is contained in:
Robert Bendun 2022-05-25 03:14:08 +02:00
parent 611007a888
commit 4421acebd6
4 changed files with 75 additions and 11 deletions

View File

@ -23,6 +23,11 @@ Arytmetyka
(aktualnie brak wsparcia dla precedensji operatorów)
3 * (4 * 12 - 8)
Przesuwanie dźwięków o półtony
c + 1 == c#
c - 1 == b
c12 + 1 == d12
Wywołanie funkcji
foo 1 2 3

View File

@ -10,8 +10,8 @@ constexpr auto binary_operator()
return [](Interpreter&, std::vector<Value> args) -> Result<Value> {
auto result = std::move(args.front());
for (auto &v : std::span(args).subspan(1)) {
assert(result.type == Value::Type::Number, "LHS should be a number");
assert(v.type == Value::Type::Number, "RHS should be a number");
assert(result.type == Value::Type::Number, "LHS should be a number"); // TODO(assert)
assert(v.type == Value::Type::Number, "RHS should be a number"); // TODO(assert)
if constexpr (std::is_same_v<Number, std::invoke_result_t<Binary_Operation, Number, Number>>) {
result.n = Binary_Operation{}(std::move(result.n), std::move(v).n);
} else {
@ -36,7 +36,7 @@ template<typename Binary_Predicate>
constexpr auto comparison_operator()
{
return [](Interpreter&, std::vector<Value> args) -> Result<Value> {
assert(args.size() == 2, "(in)Equality only allows for 2 operands"); // TODO(assert)
assert(args.size() == 2, "Ordering only allows for 2 operands"); // TODO(assert)
assert(args.front().type == args.back().type, "Only values of the same type can be ordered"); // TODO(assert)
switch (args.front().type) {
@ -118,6 +118,42 @@ static inline Result<void> create_chord(std::vector<Note> &chord, Interpreter &i
return {};
}
/// Creates implementation of plus/minus operator that support following operations:
/// number, number -> number (standard math operations)
/// n: number, m: music -> music
/// m: music, n: number -> music moves m by n semitones (+ goes up, - goes down)
template<typename Binary_Operation>
[[gnu::always_inline]]
static inline auto plus_minus_operator()
{
return [](Interpreter&, std::vector<Value> args) -> Result<Value> {
assert(args.size() == 2, "Binary operator only accepts 2 arguments");
auto lhs = std::move(args.front());
auto rhs = std::move(args.back());
if (lhs.type == rhs.type && lhs.type == Value::Type::Number) {
return Value::from(Binary_Operation{}(std::move(lhs).n, std::move(rhs).n));
}
if (lhs.type == Value::Type::Music && rhs.type == Value::Type::Number) {
music_number_operation:
for (auto &note : lhs.chord.notes) {
note.base = Binary_Operation{}(note.base, rhs.n.as_int());
note.simplify_inplace();
}
return lhs;
}
if (lhs.type == Value::Type::Number && rhs.type == Value::Type::Music) {
std::swap(lhs, rhs);
goto music_number_operation;
}
assert(false, "Unsupported types for this operation"); // TODO(assert)
unreachable();
};
}
Interpreter::Interpreter()
{
{ // Context initialization
@ -189,8 +225,8 @@ Interpreter::Interpreter()
return Value::from(std::move(chord));
});
operators["+"] = binary_operator<std::plus<>>();
operators["-"] = binary_operator<std::minus<>>();
operators["+"] = plus_minus_operator<std::plus<>>();
operators["-"] = plus_minus_operator<std::minus<>>();
operators["*"] = binary_operator<std::multiplies<>>();
operators["/"] = binary_operator<std::divides<>>();

View File

@ -472,7 +472,7 @@ struct Block
struct Note
{
/// Base of a note, like `c` (=0), `c#` (=1) `d` (=2)
u8 base;
i32 base;
/// Octave in MIDI acceptable range (from -1 to 9 inclusive)
std::optional<i8> octave = std::nullopt;
@ -490,9 +490,11 @@ struct Note
u8 into_midi_note(i8 default_octave) const;
bool operator==(Note const&) const;
void simplify_inplace();
};
std::ostream& operator<<(std::ostream& os, Note const& note);
std::ostream& operator<<(std::ostream& os, Note note);
struct Chord
{

View File

@ -41,8 +41,15 @@ constexpr u8 note_index(Indexable<usize, char> auto const& note)
return -1;
}
constexpr std::string_view note_index_to_string(u8 note_index)
#include <iostream>
constexpr std::string_view note_index_to_string(auto note_index)
{
note_index %= 12;
if (note_index < 0) {
note_index = 12 + note_index;
}
switch (note_index) {
case 0: return "c";
case 1: return "c#";
@ -178,6 +185,7 @@ Result<Value> Value::operator()(Interpreter &i, std::vector<Value> args)
if (args.size() == 2) {
note.length = args[1].n;
}
note.simplify_inplace();
}
return *this;
@ -380,8 +388,19 @@ u8 Note::into_midi_note(i8 default_octave) const
return (octave + 1) * 12 + base;
}
std::ostream& operator<<(std::ostream& os, Note const& note)
void Note::simplify_inplace()
{
if (octave) {
octave = std::clamp(*octave + int(base / 12), -1, 9);
if ((base %= 12) < 0) {
base = 12 + base;
}
}
}
std::ostream& operator<<(std::ostream& os, Note note)
{
note.simplify_inplace();
os << note_index_to_string(note.base);
if (note.octave) {
os << ":oct=" << int(*note.octave);
@ -416,13 +435,15 @@ Chord Chord::from(std::string_view source)
std::ostream& operator<<(std::ostream& os, Chord const& chord)
{
os << "chord[";
if (chord.notes.size() == 1) {
return os << chord.notes.front();
}
os << "chord[";
for (auto it = chord.notes.begin(); it != chord.notes.end(); ++it) {
os << *it;
if (std::next(it) != chord.notes.end())
os << "; ";
}
return os << ']';
}