Projekt OO, jak modelować Tonal Harmony?

12

Zacząłem pisać program w C ++ 11, który analizowałby akordy, skale i harmonię. Największym problemem, jaki mam w fazie projektowania, jest to, że nuta „C” jest nutą, rodzajem akordu (Cmaj, Cmin, C7 itd.) I rodzajem klucza (klawisz Cmajor, Cminor). Ten sam problem pojawia się w przypadku interwałów (trzeci mniejszy, trzeci większy).

Używam klasy podstawowej, Token, która jest klasą podstawową dla wszystkich „symboli” w programie. na przykład:

class Token {
public:
    typedef shared_ptr<Token> pointer_type;
    Token() {}
    virtual ~Token() {}
};

class Command : public Token {
public:
    Command() {}
    pointer_type execute();
}

class Note : public Token;

class Triad : public Token; class MajorTriad : public Triad; // CMajorTriad, etc

class Key : public Token; class MinorKey : public Key; // Natural Minor, Harmonic minor,etc

class Scale : public Token;

Jak widać, stworzenie wszystkich pochodnych klas (CMajorTriad, C, CMajorScale, CMajorKey itp.) Szybko stałoby się absurdalnie złożone, w tym wszystkie inne nuty, a także enharmoniki. wielokrotne dziedziczenie nie działałoby, tzn .:

class C : public Note, Triad, Key, Scale

klasa C nie może być jednocześnie tymi wszystkimi rzeczami. Jest kontekstowy, również polimorfizacja z tym nie zadziała (jak ustalić, które super metody wykonać? Wywołanie każdego konstruktora superklasy nie powinno się tutaj zdarzyć)

Czy są jakieś pomysły lub sugestie projektowe, które ludzie mają do zaoferowania? Nie byłem w stanie znaleźć niczego w Google w odniesieniu do modelowania harmonii tonalnej z perspektywy OO. Istnieje zbyt wiele relacji między wszystkimi koncepcjami tutaj.

Igneous01
źródło
8
Dlaczego „C” byłoby klasą? Wyobrażam sobie, że „Uwaga”, „Akord” itp. Byłyby klasami, które mogłyby mieć wyliczenie wartości, w których wyliczenie „C” mogłoby odgrywać rolę.
Rotem,
Jeśli użytkownik wprowadzi-> CEG akordu, będzie musiał wydedukować, jakie nuty mają utworzyć odpowiedni akord. Myślałem o przekazaniu wektora <Notes> jako paramów do metody execute (), która byłaby obsługiwana polimorficznie. Jednak użycie modułu wyliczającego miałoby sens, ale musiałbym utworzyć instancję każdego obiektu z wyliczeniem, którego chcę użyć.
Igneous01,
Jestem z @Rotem w tej sprawie: Czasami musisz po prostu preferować kompozycję obiektu niż dziedziczenie.
Spoike,
Wydaje mi się, że warto zastanowić się, co chcesz zrobić z tymi klasami nut / akordów / skali. Czy zamierzasz produkować nuty? Pliki Midi? Czy przekształcenia partytur (transpozycja, podwojenie wszystkich długości nut, dodanie trylów do wszystkich całych nut powyżej określonej nuty itp.)? Kiedy będziesz mieć możliwą strukturę klas, zastanów się, jak byś wykonał te zadania. Jeśli wydaje się to niezręczne, być może potrzebujesz innej struktury klas.
MatrixFrog,

Odpowiedzi:

9

Myślę, że najlepszym podejściem jest odtworzenie prawdziwych relacji między tymi podmiotami.

Na przykład możesz mieć:

  • Noteobiekt, którego właściwości są

    • nazwa (C, D, E, F, G, A, B)

    • przypadkowy (naturalny, płaski, ostry)

    • częstotliwość lub inny unikalny identyfikator wysokości tonu

  • Chordobiekt, którego właściwości są

    • tablica Noteobiektów

    • Nazwa

    • przypadkowy

    • jakość (duża, niewielka, obniżona, rozszerzona, zawieszona)

    • dodatki (7, 7+, 6, 9, 9+, 4)

  • Scaleobiekt, którego właściwości są

    • tablica Noteobiektów

    • Nazwa

    • rodzaj (major, natural minor, melodic minor, harmonic minor)

    • tryb (joński, dorycki, frygijski, lidian, mixolidian, eolski, locrian)

Następnie, jeśli dane wejściowe są tekstowe, możesz tworzyć notatki zawierające ciąg znaków, w tym nazwę notatki, przypadkowe i (jeśli potrzebujesz) oktawę.

Na przykład (pseudokod, nie znam C ++):

note = new Note('F#2');

Następnie w Noteklasie możesz przeanalizować ciąg i ustawić właściwości.

A Chordmożna zbudować na podstawie jego notatek:

chord = new Chord(['C2', 'E2', 'G2']);

... lub według ciągu zawierającego nazwę, jakość i dodatkowe uwagi:

chord = new Chord('Cmaj7');

Nie wiem, co dokładnie zrobi Twoja aplikacja, więc to tylko pomysły.

Powodzenia w fascynującym projekcie!

lortabak
źródło
4

Kilka ogólnych porad.


Jeśli oczekuje się dużej niepewności w projektowaniu klas (np. W twojej sytuacji), zaleciłbym eksperymentowanie z różnymi konkurencyjnymi projektami klas.

Używanie C ++ na tym etapie może nie być tak wydajne jak inne języki. (Kwestia ta jest widoczna w twoim fragmenty kodu konieczności zajmowania się typedefi virtualdestruktorów.) Nawet jeśli celem projektu jest stworzenie kodu C ++, może to być produktywnym zrobić wstępny projekt klasy w innym języku. (Na przykład Java, choć istnieje wiele możliwości.)

Nie wybieraj C ++ tylko z powodu wielokrotnego dziedziczenia. Dziedziczenie wielokrotne ma swoje zastosowanie, ale nie jest to właściwy sposób modelowania tego problemu (teoria muzyki).


Zwróć szczególną uwagę na jednoznaczność. Mimo że w opisach angielskich (tekstowych) występuje wiele niejasności, należy je rozwiązać przy projektowaniu klas OOP.

Mówimy o nutach G i G jako nutach. Mówimy o G-dur i G-moll jako skalach. Tak więc, Notei Scalenie są wymienne pojęcia. Nie może być żadnego obiektu, który może być jednocześnie instancją Noteai a Scale.

Ta strona zawiera kilka diagramów ilustrujących związek: http://www.howmusicworks.org/600/ChordScale-Relations/Chord-and-Scale-Relations

Na przykład: „triada rozpoczynająca się od G w skali C-dur ” nie ma tego samego znaczenia co „triada rozpoczynająca się od C w skali G-dur ”.

Na tym wczesnym etapie Tokenklasa (nadklasa wszystkiego) jest nieuzasadniona, ponieważ zapobiega jednoznaczności. W razie potrzeby można go wprowadzić później (obsługiwany przez fragment kodu, który pokazuje, jak może to być przydatne).


Na początek zacznij od Noteklasy, która jest centrum diagramu klas, a następnie stopniowo dodawaj relacje (fragmenty danych, które muszą być powiązane z krotkami Notes) do diagramu relacji klas.

C uwaga jest instancją Noteklasy. C Uwaga powróci właściwości, które są związane z tym zauważyć, takich jak związane z triad i jej względnego położenia ( Interval), w odniesieniu do Scależe rozpoczyna z C Uwaga.

Relacje między instancjami tej samej klasy (na przykład między notatką C i notatką E ) powinny być modelowane jako właściwości, a nie dziedziczenie.

Co więcej, wiele relacji międzyklasowych w twoich przykładach jest również bardziej odpowiednio modelowanych jako właściwości. Przykład:

(przykłady kodu są w toku, ponieważ muszę ponownie nauczyć się teorii muzyki ...)

rwong
źródło
Ciekawa myśl, ale jak poradzić sobie z określeniem jakości akordów w kontekście analizy harmonicznej? C Wystąpienie akordu musiałoby mieć właściwość wysokiej jakości, ustawione na drobne (co jest w porządku), ale co z dominującymi / zmniejszonymi / rozszerzonymi / małymi akordami 7, 9, 11? Istnieje wiele akordów, do których może należeć jedna nuta. Jak miałbym określić, jakie różne typy akordów i ich odpowiednie cechy znajdują się w sekcji analizy kodu?
Igneous01,
Znam bardzo mało teorii muzyki, więc nie jestem w stanie odpowiedzieć na twoje pytanie. Jednym ze sposobów, który może pomóc mi zrozumieć, jest znalezienie tabeli z listą wszystkich notatek związanych z tymi pojęciami. Zapytania dotyczące akordów mogą wymagać dodatkowych parametrów.
rwong,
2
Oto bardzo ładna lista wszystkich możliwych akordów: en.wikipedia.org/wiki/List_of_chords Wszystkie akordy można zastosować do dowolnej nuty, co jest ważne w mojej sytuacji, to, że enharmoniki są poprawne: tj. Cflat major! = BMajor, są one fizycznie tym samym akordem na fortepianie, ale ich funkcje harmoniczne są bardzo różne na papierze. Myślę, że wylicznik do wyostrzania / spłaszczania nuty miałby sens w przypadku nuty. W ten sposób C.Sharpen () = C # i C.Flatten () = Cb, może to ułatwić mi sprawdzanie akordów użytkownika.
Igneous01,
2

Zasadniczo nuty to częstotliwości, a interwały muzyczne to stosunki częstotliwości.

Na tym można zbudować wszystko inne.

Akord to lista interwałów. Skala jest podstawową nutą i systemem strojenia. System strojenia to także lista interwałów.

Jak je nazwiesz, to tylko kulturowy artefakt.

Artykuł na temat teorii muzyki w Wikipedii jest dobrym punktem wyjścia.

mouviciel
źródło
Ciekawe, chociaż nie jestem pewien, czy modelowanie systemu w kategoriach fizycznej rzeczywistości jest pomocne. Pamiętaj, że model musi być pomocny w określaniu jednego konkretnego aspektu, niekoniecznie musi być kompleksowy, a nawet dokładny. Chociaż twoje podejście byłoby zarówno dokładne, jak i kompleksowe, może być zbyt niski dla przypadku użycia OP.
Konrad Rudolph
@KonradRudolph - W mojej ekstremalnej pozycji chciałem tylko zaznaczyć, że nie należy mieszać modelu leżącego u podstaw z warstwą prezentacji, podobnie jak w przypadku czasu letniego: Obliczenia są znacznie łatwiejsze dla samego modelu. Zgadzam się, że najbardziej pomocny poziom abstrakcji nie jest tym, co sugeruję, ale uważam, że poziom abstrakcji sugerowany przez PO również nie jest odpowiedni.
mouviciel
Celem tego programu nie jest koniecznie wyświetlanie fizycznej rzeczywistości muzyki. Ale dla osób studiujących teorię (takich jak ja), aby móc po prostu szybko wykreślić niektóre akordy i pozwolić programowi zinterpretować w najlepszy sposób, w jaki sposób te akordy są powiązane ze sobą w sensie harmonicznym. Mógłbym po prostu przeanalizować wypróbowany i prawdziwy sposób odczytywania wyniku miara według miary, ale jest to kolejne narzędzie, aby ułatwić i skupić się na bardziej szczegółowych szczegółach podczas analizy.
Igneous01,
1

Uważam tę dyskusję za fascynującą.

Czy nuty są wprowadzane za pomocą midi (lub innego rodzaju urządzenia do przechwytywania tonów), czy też wprowadza się je, wpisując litery i symbole?

W przypadku przedziału od C do D-ostre / E-płaskie:

Chociaż D-ostry i E-płaski mają ten sam ton (około 311 Hz, jeśli A = 440 Hz), interwał od C -> D-ostry zapisywany jest jako powiększony 2., zaś interwał od C -> E-płaski jest zapisywany jako drobne 3.. Łatwo, jeśli wiesz, jak napisano notatkę. Nie można ustalić, czy masz tylko dwa dźwięki.

W tym przypadku uważam, że będziesz potrzebować sposobu na zwiększenie / zmniejszenie tonu wraz z wymienionymi metodami .Sharpen () i .Flatten (), takimi jak .SemiToneUp (), .FullToneDown () itp., Więc że można znaleźć kolejne nuty w skali bez „kolorowania” ich jako ostrych / płaskich.

Muszę zgodzić się z @Rotem, że „C” nie jest klasą samą w sobie, ale raczej instancją klasy Note.

Jeśli zdefiniujesz właściwości nuty, w tym wszystkie interwały jako półtonów, to niezależnie od początkowej wartości nuty („C”, „F”, „G #”) będziesz w stanie powiedzieć, że sekwencja trzech nut, która ma root, major 3rd (M3), a następnie minor 3rd (m3) byłby główną triadą. Podobnie m3 + M3 jest niewielką triadą, m3 + m3 zmniejszony, M3 + M3 powiększony. Dodatkowo dałoby to sposób na enkapsulację znalezienia 11, zmniejszonego 13 itd. Bez wyraźnego kodowania ich dla wszystkich 12 nut bazowych i ich oktaw w górę i w dół.

Gdy to zrobisz, wciąż masz problemy do rozwiązania.

Weź triadę C, E, G. Jako muzyk widzę to wyraźnie jako akord Cmaj. Jednak deweloper we mnie może interpretować to dodatkowo jako e-moll Augment 5 (Root E + m3 + a5) lub Gsus4 6th no 5th (RootG + 4 + 6).

Tak więc, aby odpowiedzieć na twoje pytanie dotyczące wykonywania analizy, myślę, że najlepszym sposobem na określenie modalności (maj, minor, itp.) Byłoby pobranie wszystkich wprowadzonych nut, ułożenie ich według rosnącej wartości półtonu i przetestowanie ich pod kątem znanych form akordów . Następnie użyj każdej nuty wpisanej jako nuta główna i wykonaj ten sam zestaw ocen.

Można tak wyważyć formy akordów, aby bardziej popularne (większe, mniejsze) miały pierwszeństwo przed formami akordów rozszerzonymi, zawieszonymi, elektra itp., Ale dokładna analiza wymagałaby przedstawienia wszystkich pasujących form akordów jako możliwych rozwiązań.

Znów wspomniany artykuł na Wikipedii dobrze spisuje klasy boisk, więc powinno być proste (choć żmudne) kodowanie modeli akordów, zapisywanie wprowadzonych notatek, przypisywanie ich do klas / interwałów pitch, a następnie porównywanie przeciwko znanym formom meczów.

To była świetna zabawa. Dzięki!

Jan
źródło
Są one teraz wprowadzane za pośrednictwem tekstu. Jednak później mogę używać midi, jeśli program jest poprawnie zamknięty. Baby kroki teraz: D
Igneous01
0

Brzmi jak skrzynka na szablony. To wydaje się mieć template <?> class Major : public Chord;tak Major<C>jest-a Chord, jak to jest Major<B>. Podobnie masz Note<?>szablon z instancjami Note<C>i Note<D>.

Jedyne, co pominąłem, to ?część. Wygląda na to, że masz, enum {A,B,C,D,E,F,G}ale nie wiem, jak nazwałbyś to wyliczenie.

MSalters
źródło
0

Dziękuję za wszystkie sugestie, jakoś udało mi się przeoczyć dodatkowe odpowiedzi. Do tej pory moje klasy były tak zaprojektowane:

Note
enum Qualities - { DFLAT = -2, FLAT, NATURAL, SHARP, DSHARP }
char letter[1] // 1 char letter
string name // real name of note
int value // absolute value, the position on the keyboard for a real note (ie. c is always 0)
int position // relative position on keyboard, when adding sharp/flat, position is modified
Qualities quality // the quality of the note ie sharp flat

Aby rozwiązać problemy z obliczaniem interwałów i akordów, zdecydowałem się użyć bufora kołowego, który pozwala mi przechodzić przez bufor z dowolnego punktu, idąc do przodu, aż znajdę następną pasującą nutę.

Aby znaleźć interpretowany interwał, przejdź do bufora rzeczywistych nut, zatrzymaj się, gdy litery będą pasować (tylko litera, a nie faktyczna nuta lub pozycja), więc c - g # = 5

Aby znaleźć rzeczywiste przejście przez kolejny bufor 12 liczb całkowitych, zatrzymaj się, gdy pozycja górnej nuty jest taka sama, jak wartość bufora na indeksie, ponownie jest to tylko ruch do przodu. Ale offset może być w dowolnym miejscu (np. Buffer.at (-10))

teraz znam zarówno interpretowany przedział, jak i fizyczną odległość między nimi. więc nazwa interwału jest już w połowie ukończona.

teraz jestem w stanie zinterpretować interwał, tj. jeśli interwał wynosi 5, a odległość wynosi 8, oznacza to, że jest to 5-ty stopień.

Do tej pory nuta i interwał działały zgodnie z oczekiwaniami, teraz muszę tylko zmierzyć się z identyfikatorem akordu.

Jeszcze raz dziękuję, przeczytam ponownie niektóre z tych odpowiedzi i przedstawię tutaj kilka pomysłów.

Igneous01
źródło