Halftones moving operator
This commit is contained in:
parent
611007a888
commit
4421acebd6
@ -23,6 +23,11 @@ Arytmetyka
|
|||||||
(aktualnie brak wsparcia dla precedensji operatorów)
|
(aktualnie brak wsparcia dla precedensji operatorów)
|
||||||
3 * (4 * 12 - 8)
|
3 * (4 * 12 - 8)
|
||||||
|
|
||||||
|
Przesuwanie dźwięków o półtony
|
||||||
|
c + 1 == c#
|
||||||
|
c - 1 == b
|
||||||
|
c12 + 1 == d12
|
||||||
|
|
||||||
Wywołanie funkcji
|
Wywołanie funkcji
|
||||||
foo 1 2 3
|
foo 1 2 3
|
||||||
|
|
||||||
|
@ -10,8 +10,8 @@ constexpr auto binary_operator()
|
|||||||
return [](Interpreter&, std::vector<Value> args) -> Result<Value> {
|
return [](Interpreter&, std::vector<Value> args) -> Result<Value> {
|
||||||
auto result = std::move(args.front());
|
auto result = std::move(args.front());
|
||||||
for (auto &v : std::span(args).subspan(1)) {
|
for (auto &v : std::span(args).subspan(1)) {
|
||||||
assert(result.type == Value::Type::Number, "LHS 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");
|
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>>) {
|
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);
|
result.n = Binary_Operation{}(std::move(result.n), std::move(v).n);
|
||||||
} else {
|
} else {
|
||||||
@ -36,7 +36,7 @@ template<typename Binary_Predicate>
|
|||||||
constexpr auto comparison_operator()
|
constexpr auto comparison_operator()
|
||||||
{
|
{
|
||||||
return [](Interpreter&, std::vector<Value> args) -> Result<Value> {
|
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)
|
assert(args.front().type == args.back().type, "Only values of the same type can be ordered"); // TODO(assert)
|
||||||
|
|
||||||
switch (args.front().type) {
|
switch (args.front().type) {
|
||||||
@ -118,6 +118,42 @@ static inline Result<void> create_chord(std::vector<Note> &chord, Interpreter &i
|
|||||||
return {};
|
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()
|
Interpreter::Interpreter()
|
||||||
{
|
{
|
||||||
{ // Context initialization
|
{ // Context initialization
|
||||||
@ -189,8 +225,8 @@ Interpreter::Interpreter()
|
|||||||
return Value::from(std::move(chord));
|
return Value::from(std::move(chord));
|
||||||
});
|
});
|
||||||
|
|
||||||
operators["+"] = binary_operator<std::plus<>>();
|
operators["+"] = plus_minus_operator<std::plus<>>();
|
||||||
operators["-"] = binary_operator<std::minus<>>();
|
operators["-"] = plus_minus_operator<std::minus<>>();
|
||||||
operators["*"] = binary_operator<std::multiplies<>>();
|
operators["*"] = binary_operator<std::multiplies<>>();
|
||||||
operators["/"] = binary_operator<std::divides<>>();
|
operators["/"] = binary_operator<std::divides<>>();
|
||||||
|
|
||||||
|
@ -472,7 +472,7 @@ struct Block
|
|||||||
struct Note
|
struct Note
|
||||||
{
|
{
|
||||||
/// Base of a note, like `c` (=0), `c#` (=1) `d` (=2)
|
/// 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)
|
/// Octave in MIDI acceptable range (from -1 to 9 inclusive)
|
||||||
std::optional<i8> octave = std::nullopt;
|
std::optional<i8> octave = std::nullopt;
|
||||||
@ -490,9 +490,11 @@ struct Note
|
|||||||
u8 into_midi_note(i8 default_octave) const;
|
u8 into_midi_note(i8 default_octave) const;
|
||||||
|
|
||||||
bool operator==(Note const&) 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
|
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;
|
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) {
|
switch (note_index) {
|
||||||
case 0: return "c";
|
case 0: return "c";
|
||||||
case 1: return "c#";
|
case 1: return "c#";
|
||||||
@ -178,6 +185,7 @@ Result<Value> Value::operator()(Interpreter &i, std::vector<Value> args)
|
|||||||
if (args.size() == 2) {
|
if (args.size() == 2) {
|
||||||
note.length = args[1].n;
|
note.length = args[1].n;
|
||||||
}
|
}
|
||||||
|
note.simplify_inplace();
|
||||||
}
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
@ -380,8 +388,19 @@ u8 Note::into_midi_note(i8 default_octave) const
|
|||||||
return (octave + 1) * 12 + base;
|
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);
|
os << note_index_to_string(note.base);
|
||||||
if (note.octave) {
|
if (note.octave) {
|
||||||
os << ":oct=" << int(*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)
|
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) {
|
for (auto it = chord.notes.begin(); it != chord.notes.end(); ++it) {
|
||||||
os << *it;
|
os << *it;
|
||||||
if (std::next(it) != chord.notes.end())
|
if (std::next(it) != chord.notes.end())
|
||||||
os << "; ";
|
os << "; ";
|
||||||
}
|
}
|
||||||
|
|
||||||
return os << ']';
|
return os << ']';
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user