OO Design, як моделювати Tonal Harmony?


12

Я почав писати програму на C ++ 11, яка б аналізувала акорди, масштаби та гармонію. Найбільшою проблемою, що виникає у мене на етапі проектування, є те, що нота "C" - це нота, тип акорду (Cmaj, Cmin, C7 тощо) та тип клавіші (ключ Cmajor, Cminor). Це ж питання виникає з інтервалами (другорядний 3-й, основний 3-й).

Я використовую базовий клас Token, який є базовим класом для всіх «символів» у програмі. так наприклад:

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;

Як бачимо, створення всіх похідних класів (CMajorTriad, C, CMajorScale, CMajorKey тощо) швидко стало б смішно складним, включаючи всі інші нотатки, а також енгармоніки. багаторазове успадкування не буде працювати, тобто:

class C : public Note, Triad, Key, Scale

клас C, не можуть бути всі ці речі одночасно. Це контекстуально, також поліморфінг з цим не спрацює (як визначити, які супер методи виконувати? Виклик усіх конструкторів суперкласу тут не повинен відбуватися)

Чи є якісь дизайнерські ідеї чи пропозиції, які люди можуть запропонувати? Мені не вдалося знайти нічого в Google щодо моделювання тональної гармонії з точки зору ОО. Тут існує дуже багато взаємозв'язків між усіма поняттями.


8
Чому "C" - клас? Я б уявив, що "Note", "акорд" і т.д. були б класами, які могли б мати перерахування значення, в якому перелік "C" може зіграти роль.
Ротем

Якщо користувач вводить-> акорд CEG, йому потрібно буде вивести те, що ноти, щоб сформувати відповідний акорд. Я думав передати вектор <Notes> як парами методу Execute (), який би вирішувався поліморфно. Однак використання перелічувача має сенс, але тоді мені потрібно інстанціювати кожен об'єкт із перерахунком, який я хочу використовувати.
Igneous01

Я з @Rotem на цьому: Іноді просто потрібно віддавати перевагу композиції об’єктів перед успадкуванням.
Спіке

Мені здається, що може бути корисним подумати про те, що ви хочете зробити з цими класами нота / акорд / масштаб. Ви збираєтеся виробляти ноти? Файли Midi? Чи проводяться перетворення на балах (переміщення, подвоєння всіх довжин нот, додавання трелів до всіх цілих нот над певною нотою тощо)? Отримавши можливу структуру класу, подумайте, як би ви виконали ці завдання. Якщо це здається незручним, можливо, вам потрібна інша структура класу.
MatrixFrog

Відповіді:


9

Я думаю, що найкращим підходом є відтворення реальних відносин між цими сутностями.

Наприклад, ви можете мати:

  • Noteоб'єкт, властивість якого

    • назва (C, D, E, F, G, A, B)

    • випадкові (природні, плоскі, різкі)

    • частоти або іншого унікального ідентифікатора тону

  • Chordоб'єкт, властивість якого

    • масив Noteоб’єктів

    • назва

    • випадковий

    • якість (основна, незначна, зменшена, збільшена, призупинена)

    • доповнення (7, 7+, 6, 9, 9+, 4)

  • Scaleоб'єкт, властивість якого

    • масив Noteоб’єктів

    • назва

    • тип (основний, природний мінор, мелодичний мінор, гармонічний мінор)

    • режим (іонічний, дорійський, фрігійський, лідійський, міксолідійський, еолійський, локрійський)

Потім, якщо ваш текст текстовий, ви можете створювати нотатки з рядком, включаючи назву нотатки, випадкову та (якщо вам це потрібно) октаву.

Наприклад (псевдокод, я не знаю C ++):

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

Потім у Noteкласі ви можете проаналізувати рядок та встановити властивості.

A Chordможе бути побудований за його примітками:

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

... або рядком, що включає ім'я, якість та додаткові примітки:

chord = new Chord('Cmaj7');

Я не знаю, що саме зробить ваша програма, тому це лише ідеї.

Удачі у вашому захоплюючому проекті!


4

Деякі загальні поради.


Якщо в дизайні класу очікується велика невизначеність (наприклад, у вашій ситуації), я рекомендую експериментувати з різними конкуруючими проектами класів.

Використання C ++ на цьому етапі може бути не таким продуктивним, як інші мови. (Ця проблема проявляється в ваших фрагменти коди, щоб мати справу з typedefі virtualдеструкторами.) Навіть якщо метою проекту є створення C ++ коду, він може бути продуктивним , щоб зробити початковий дизайн класу на іншій мові. (Наприклад, Java, хоча є багато варіантів.)

Не вибирайте C ++ лише через багаторазове успадкування. Багатократне успадкування має свою користь, але це не правильний спосіб моделювання цієї проблеми (теорія музики).


Зверніть особливу увагу на розбіжність. Незважаючи на те, що двозначності багато в англійських (текстових) описах, ці неоднозначності повинні бути вирішені під час проектування класів OOP.

Ми говоримо про G і G різкі як ноти. Ми говоримо про G мажор і G мінор як про масштаби. Таким чином, Noteі Scaleне є взаємозамінними поняттями. Не може бути жодного об'єкта, який може бути одночасно екземпляром a Noteі a Scale.

Ця сторінка містить кілька діаграм, що ілюструють взаємозв'язок: http://www.howmusicworks.org/600/ChordScale-Relations/Chord-and-Scale-Relations

Для іншого прикладу, "Тріада, яка починається з G за великою шкалою С ", не має того ж значення, що і "Тріада, яка починається з C у великій шкалі G ".

На цьому ранньому етапі Tokenклас (надклас усього) є необґрунтованим, оскільки він перешкоджає розбіжності. Він може бути введений пізніше за потреби (підтримується фрагментом коду, який демонструє, як це може бути корисним.)


Для початку почніть з Noteкласу, який є центром діаграми класів, а потім поступово додайте відносини (фрагменти даних, які потрібно пов’язати з кортежами Notes) до діаграми відносин класу.

C нота є екземпляром Noteкласу. З нотою буде повертати властивості , які мають відношення до цієї записці, такі , як пов'язані з тріадами, і його відносне положення ( Interval) по відношенню до , Scaleякий починається з C нотою.

Взаємовідносини між екземплярами одного класу (наприклад, між приміткою C і приміткою E ) повинні моделюватися як властивості, а не спадкування.

Більше того, багато міжкласових відносин у ваших прикладах також більш доцільно моделюються як властивості. Приклад:

(приклади коду очікують, тому що мені потрібно вивчити теорію музики ...)


Цікава думка, але як би вирішити визначення якостей акордів у контексті гармонічного аналізу? C Екземпляр акорду повинен мати властивість якості, встановлену на мінор (що нормально), але що робити з домінуючим / зменшеним / розширеним / мінорним 7, 9, 11 акордами? Є багато акордів, до яких може належати одна нота. Як я можу визначити, які різні типи акордів та їх відповідні якості містяться в розділі аналізу коду?
Igneous01

Я знаю дуже мало теорії музики, тому не можу відповісти на ваше запитання. Один із способів, який допоможе мені зрозуміти, - це знайти таблицю, в якій перераховані всі примітки до цих концепцій. Запити для акордів можуть приймати додаткові параметри.
rwong

2
Ось дуже приємний список усіх можливих акордів: en.wikipedia.org/wiki/List_of_chords Всі акорди можна застосувати до будь-якої ноти, що важливо для моєї ситуації, щоб енгармоніки були правильними: тобто. Cflat major! = BMajor, Фізично вони однакові акорди на фортепіано, але їх гармонічні функції сильно відрізняються на папері. Я думаю, що перелік для різкості / вирівнювання ноти мав би сенс для екземпляра ноти. Таким чином C.Sharpen () = C # і C.Flatten () = Cb, це може полегшити мені перевірку акордів користувача.
Igneous01

2

В основному музичні ноти - це частоти, а музичні інтервали - співвідношення частот.

На цьому все можна побудувати.

Акорд - це список інтервалів. Шкала - це основна примітка та система настройки. Система настройки - це також перелік інтервалів.

Як ви їх називаєте - це лише культурний артефакт.

Стаття з теорії музики Вікіпедії - хороший вихідний пункт.


Цікаво, хоча я не впевнений, що це обов'язково корисно моделювати систему з точки зору основної фізичної реальності. Пам'ятайте, що модель повинна бути корисною при формулюванні одного конкретного аспекту, не обов'язково бути вичерпною або навіть точною. Хоча ваш підхід буде одночасно точним та всеосяжним, він може бути занадто низьким для випадку використання ОП.
Конрад Рудольф

@KonradRudolph - З моїм крайнім становищем я просто хотів зазначити, що не слід змішувати базову модель із шаром презентації таким чином, як літній час: обчислення набагато простіші для самої моделі. Я погоджуюся, що найбільш корисним рівнем абстракції є не те, що я пропоную, але я вважаю, що рівень абстракції, запропонований ОП, також не є адекватним.
mouviciel

Мета цієї програми - не обов’язково відображати фізичну реальність музики. Але для людей, що вивчають теорію (як я), можна просто швидко побудувати графік у деяких акордах і дозволити програмі тлумачити найкращі можливості, як ці акорди пов'язані один з одним у гармонійному розумінні. Я міг би просто проаналізувати це випробуваним і правдивим способом читання балів за мірою, але це ще один інструмент для спрощення речей і для того, щоб мати можливість зосередитись на більш тонких деталях при аналізі.
Igneous01

1

Я вважаю цю дискусію захоплюючою.

Чи вводяться нотатки через міді (або якийсь тип пристрою захоплення тонів) чи вони вводяться, вводячи літери та символи?

У випадку інтервалу від C до D-гострий / E-плоский:

Хоча D-різкий і E-плоскі однакові (приблизно 311 Гц, якщо А = 440 Гц), інтервал від С -> Д-різкий пишеться збільшеним 2-м, тоді як інтервал від С -> Е-плоскою записаний як a другорядний 3-й. Досить просто, якщо ви знаєте, як була написана записка. Неможливо визначити, чи потрібно тривати лише два тони.

У цьому випадку я вважаю, що вам також знадобиться спосіб підвищення / зменшення тону разом із згаданими методами .Sharpen () та .Flatten (), такими як .SemiToneUp (), .FullToneDown () тощо. що ви можете знайти підрядні нотки в масштабі, не «розфарбовуючи» їх як гострі / плоскі.

Я маю згоду з @Rotem, що "C" - це не сам по собі клас, а скоріше інстанція класу Note.

Якщо ви визначите властивості для ноти, включаючи всі інтервали як півтони, то незалежно від початкового значення ноти ("C", "F", "G #"), ви зможете сказати, що три послідовності нотатки мають корінь, основний 3-й (М3), тоді мінорний 3-й (м3) був би головною тріадою. Аналогічно, м3 + М3 - незначна тріада, м3 + м3 зменшено, М3 + М3 - збільшена. Крім того, це дасть вам змогу інкапсулювати пошук 11-ої, зменшеної 13-ї тощо тощо, не чітко кодуючи їх для всіх 12 базових нот та їх октав вгору та вниз.

Після цього ви все ще можете вирішити деякі проблеми.

Візьміть тріаду C, E, G. Як музикант, це я чітко бачу як акорд Cmaj. Однак розробник в мені може інтерпретувати це додатково як E мінорний додаток 5 (Root E + m3 + a5) або Gsus4 6th no 5th (RootG + 4 + 6).

Отже, щоб відповісти на ваше запитання про аналіз, я думаю, що найкращим способом визначення модальності (май, мінор тощо) було б взяти всі введені нотатки, впорядкувати їх у висхідному півтоновому значенні та перевірити їх на відомі форми акордів . Потім використовуйте кожну нотатку, введену як кореневу примітку, і виконайте той самий набір оцінок.

Ви можете зважувати форми акордів, щоб більш поширені (основні, мінорні) мали перевагу над розширеними, підвішеними, електральними і т. Д. Акордовими формами, але для точного аналізу потрібно буде представити всі відповідні форми акордів як можливі рішення.

Знову згадана стаття у Вікіпедії робить хорошу роботу з переліком класів висоти, тому слід просто (хоч і втомливо) кодувати моделі акордів, робити введені нотатки, призначати їх розміщенню класів / інтервалів, а потім порівнювати проти відомих форм матчів.

Це було дуже весело. Дякую!


Вони зараз вводяться через текст. Однак пізніше я можу використовувати міді, якщо програма правильно інкапсульована. Дитячі кроки прямо зараз: D
Ігно01 01

0

Звучить як випадок для шаблонів. Здається, у вас є template <?> class Major : public Chord;такий, який Major<C>є Chord, як є Major<B>. Так само у вас є Note<?>шаблон із екземплярами Note<C>та Note<D>.

Єдине, що я залишив, - це ?частина. Здається, у вас є, enum {A,B,C,D,E,F,G}але я не знаю, як би ви назвали цю суму.


0

Дякую за всі пропозиції, якось мені вдалося пропустити зайві відповіді. Поки мої заняття були розроблені так:

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

Щоб вирішити проблеми з інтервалом і акордом, я вирішив використовувати круговий буфер, який дозволяє мені переходити буфер з будь-якої точки, рухаючись вперед, поки я не знайду наступну ноту, яка відповідає.

Щоб знайти інтерпретований інтервал - пройдіть буфер реальних нотаток, зупиніться, коли букви збігаються (лише літера, а не фактична примітка чи позиція), так c - g # = 5

Щоб знайти реальну відстань - пройдіть інший буфер з 12 цілих чисел, зупиніться, коли позиція верхньої ноти збігається з значенням буфера в індексі, і знову це рухається лише вперед. Але зміщення може бути в будь-якому місці (наприклад, buffer.at (-10))

тепер я знаю як інтерпретований інтервал, так і фізичну відстань між ними. тому назва інтервалу вже наполовину повна.

тепер я вмію інтерпретувати інтервал, тобто. якщо інтервал 5, а відстань - 8, то це розширений 5-й.

Поки нота та інтервал працюють як очікувалося, тепер мені залишається лише вирішити ідентифікатор акорда.

Ще раз дякую, я перечитаю деякі з цих відповідей і включу тут деякі ідеї.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.