Halftones moving operator
This commit is contained in:
parent
611007a888
commit
4421acebd6
@ -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
|
||||
|
||||
|
@ -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 ¬e : 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<>>();
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
|
29
src/value.cc
29
src/value.cc
@ -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 << ']';
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user