48 const int relativeOctave = octave - 4;
57constexpr inline T
sign(T n)
59 static_assert(std::is_arithmetic_v<T>,
"sign requires a numeric type");
60 return n < T(0) ? T(-1) : T(1);
70 static_assert(std::is_integral_v<T>,
"signed_modulus requires an integer type");
71 return sign(n) * (std::abs(n) % d);
84 static constexpr std::array<int, STANDARD_DIATONIC_STEPS> MAJOR_KEYMAP = { 0, 2, 4, 5, 7, 9, 11 };
85 static constexpr std::array<int, STANDARD_DIATONIC_STEPS> MINOR_KEYMAP = { 0, 2, 3, 5, 7, 8, 10 };
88 int m_numberOfEdoDivisions;
89 std::vector<int> m_keyMap;
91 static constexpr std::array<std::array<int, 2>, 7> DIATONIC_INTERVAL_ADJUSTMENTS = { {
112 const std::optional <std::vector<int>>& keyMap = std::nullopt)
117 throw std::invalid_argument(
"The Transposer class only supports key map arrays of " + std::to_string(
STANDARD_DIATONIC_STEPS) +
" elements");
119 m_keyMap = keyMap.value();
120 }
else if (isMinor) {
121 m_keyMap.assign(MINOR_KEYMAP.begin(), MINOR_KEYMAP.end());
123 m_keyMap.assign(MAJOR_KEYMAP.begin(), MAJOR_KEYMAP.end());
137 m_displacement += interval;
144 const int keyStepEnharmonic = calcStepsBetweenScaleDegrees(m_displacement, m_displacement +
sign(direction));
146 m_alteration -=
sign(direction) * keyStepEnharmonic;
173 const int stepsInAlteration = calcStepsInAlteration(interval, chromaticAlteration);
174 const int stepsInInterval = calcStepsInNormalizedInterval(intervalNormalized);
175 const int stepsInDiatonicInterval = calcStepsBetweenScaleDegrees(m_displacement, m_displacement + intervalNormalized);
177 const int effectiveAlteration = stepsInAlteration + stepsInInterval -
sign(interval) * stepsInDiatonicInterval;
180 m_alteration += effectiveAlteration;
191 while (std::abs(m_alteration) > 0) {
192 const int currSign =
sign(m_alteration);
193 const int currAbsDisp = std::abs(m_alteration);
195 if (std::abs(m_alteration) >= currAbsDisp) {
199 if (currSign !=
sign(m_alteration)) {
216 m_alteration += numberOfEdoDivisions;
231 return calcAbsoluteDivision(
displacement,
alteration) == calcAbsoluteDivision(m_displacement, m_alteration);
235 int calcFifthSteps()
const
238 static constexpr double kFifthsMultiplier = 0.5849625007211562;
239 return static_cast<int>(std::floor(m_numberOfEdoDivisions * kFifthsMultiplier) + 0.5);
242 int calcScaleDegree(
int interval)
const
244 int intervalNormalized =
signedModulus(interval,
int(m_keyMap.size()));
245 if (intervalNormalized < 0) {
246 intervalNormalized += int(m_keyMap.size());
248 return intervalNormalized;
251 int calcStepsBetweenScaleDegrees(
int firstDisplacement,
int secondDisplacement)
const
253 const int firstScaleDegree = calcScaleDegree(firstDisplacement);
254 const int secondScaleDegree = calcScaleDegree(secondDisplacement);
255 int result =
sign(secondDisplacement - firstDisplacement) * (m_keyMap[secondScaleDegree] - m_keyMap[firstScaleDegree]);
257 result += m_numberOfEdoDivisions;
262 int calcStepsInAlteration(
int interval,
int alteration)
const
264 const int fifthSteps = calcFifthSteps();
267 const int result =
sign(interval) * ((plusFifths * fifthSteps) + (minusOctaves * m_numberOfEdoDivisions));
271 int calcStepsInNormalizedInterval(
int intervalNormalized)
const
273 const int fifthSteps = calcFifthSteps();
274 const int index = std::abs(intervalNormalized);
275 const int plusFifths = DIATONIC_INTERVAL_ADJUSTMENTS[index][0];
276 const int minusOctaves = DIATONIC_INTERVAL_ADJUSTMENTS[index][1];
278 return sign(intervalNormalized) * ((plusFifths * fifthSteps) + (minusOctaves * m_numberOfEdoDivisions));
283 const int baseStep = m_keyMap[scaleDegree];
288 const int octaveSteps = octaveCount * m_numberOfEdoDivisions;
289 const int chromaticSteps = calcStepsInAlteration(+1,
alteration);
291 return baseStep + chromaticSteps + octaveSteps;
Provides dependency-free transposition utilities that work with any scale that has 7 diatonic steps a...
Definition music_theory.hpp:82
int displacement() const
Return the current displacement value.
Definition music_theory.hpp:128
void chromaticTranspose(int interval, int chromaticAlteration)
Chromatically transposes by a specified chromatic interval.
Definition music_theory.hpp:170
void stepwiseTranspose(int numberOfEdoDivisions)
Transposes by the given number of EDO divisions and simplifies the spelling.
Definition music_theory.hpp:214
int alteration() const
Return the current chromatic alteration value.
Definition music_theory.hpp:131
void simplifySpelling()
Simplifies the spelling by reducing its alteration while preserving pitch.
Definition music_theory.hpp:189
Transposer(int displacement, int alteration, bool isMinor=false, int numberOfEdoDivisions=STANDARD_12EDO_STEPS, const std::optional< std::vector< int > > &keyMap=std::nullopt)
Constructor function.
Definition music_theory.hpp:110
void diatonicTranspose(int interval)
Transposes the displacement by the specified interval.
Definition music_theory.hpp:135
bool isEnharmonicEquivalent(int displacement, int alteration) const
Determines if the given displacement and alteration refer to the same pitch as the current state.
Definition music_theory.hpp:230
void enharmonicTranspose(int direction)
Transposes enharmonically relative to the current values.
Definition music_theory.hpp:142
A dependency-free, header-only collection of useful functions for music theory.
constexpr T signedModulus(T n, T d)
Calculates the modulus of positive and negative numbers in a predictable manner.
Definition music_theory.hpp:69
constexpr T sign(T n)
Calculates the sign of an integer.
Definition music_theory.hpp:57
int calcDisplacement(int pitchClass, int octave)
Calculates the displacement value for a given absolute pitch class and octave.
Definition music_theory.hpp:45
constexpr int STANDARD_12EDO_STEPS
this can be overriden when constructing a Transposer instance.
Definition music_theory.hpp:39
constexpr int STANDARD_DIATONIC_STEPS
currently this is the only supported number of diatonic steps.
Definition music_theory.hpp:38