Skip to content

Instantly share code, notes, and snippets.

@boblucas
Last active August 8, 2019 22:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save boblucas/1b0b6bf68528708d6be1a91dff1ef3d2 to your computer and use it in GitHub Desktop.
Save boblucas/1b0b6bf68528708d6be1a91dff1ef3d2 to your computer and use it in GitHub Desktop.
#include <vector>
#include <set>
#include <array>
#include <numeric>
#include <algorithm>
#include <iostream>
#include <math.h>
// Because C++ mod is really remainder not mod
int mod(int a, int b) { return a % b + b * (a < 0); }
int diatonic_to_chromatic(int d, int a = 0) { return d * 2 - (d / 7) * 2 - (mod(d, 7) > 2) + a; }
struct Note
{
int pitch = 0, acc = 0;
std::array<unsigned, 12> chromagram = {};
int diatonic() { return (pitch - acc) / 2 + ((pitch - acc) / 12) + (mod((pitch - acc), 12) > 4); }
void set_acc_by_diatonic(int step)
{
acc = mod(pitch, 12) - diatonic_to_chromatic(mod(step, 7));
acc -= 12 * (acc > 6);
}
};
void pitchSpell(std::vector<Note>& notes, int kPre = 10, int kPost = 42)
{
for(int i = 0; i < notes.size(); i++)
for(int j = std::max(0, i - kPre); j < std::min((int)notes.size(), i + kPost); j++)
++notes[i].chromagram[notes[j].pitch % 12];
std::array<int, 12> morphInt = {0, 1, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6};
std::array<int, 12> initMorph = {0, 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 6};
int c0 = notes[0].pitch % 12;
int m0 = initMorph[c0];
std::array<int, 12> tonicMorphForTonicChroma;
for(int ct = 0; ct < 12; ct++)
tonicMorphForTonicChroma[ct] = mod(m0 - morphInt[mod(c0-ct, 12)], 7);
for(auto& note : notes)
{
std::vector<std::set<int>> chromas(7);
for(int ct = 0; ct < 12; ct++)
chromas[mod(morphInt[mod(note.pitch % 12 - ct, 12)] + tonicMorphForTonicChroma[ct], 7)].insert(ct);
int maxStrength = -1;
int maxM = 0;
for(int m = 0; m < 7; m++)
{
int strength = 0;
for(int tonicChroma : chromas[m])
strength += note.chromagram[tonicChroma];
maxM = strength > maxStrength ? m : maxM;
maxStrength = std::max(strength, maxStrength);
}
note.set_acc_by_diatonic(maxM);
std::cout << note.pitch << " " << maxM << " " << note.acc << std::endl;
}
}
int main(int argc, char** argv)
{
std::vector<int> pitches = {0, 0, 7, 7, 9, 9, 7, 7, 5, 5, 4 ,4 ,3, 3, 0, 0};
std::vector<Note> notes;
for(auto p : pitches) notes.push_back({p, 0});
pitchSpell(notes);
for(auto n : notes)
{
std::cout << "CDEFGAB"[(n.diatonic() + 7*100) % 7] << "-b #+"[n.acc + 2] << ' ';
}
return 0;
}
@BernardGreenberg
Copy link

Can you post some code that tests/demos this?

@boblucas
Copy link
Author

boblucas commented Aug 8, 2019

Sure you can do something like:

<Just added it above, because this view didn't work>

To just dump in some time ordered midi pitching and it will print back those pitches as note names + accidentals. I just did this and combined it with grep -Po "(?<=)\d+(?=)" to just get all pitches out of an imploded score, then you can do the same for the tpc values in the score and check the accuracy. It's all very hacky I apologize, just spend an hour or 2 on it to see the result.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment