オブジェクト指向デザイン、トーナルハーモニーのモデリング方法


12

コード、音階、ハーモニーを分析するプログラムをC ++ 11で書き始めました。私が設計段階で抱えている最大の問題は、音「C」が音、コードのタイプ(Cmaj、Cmin、C7など)、およびキーのタイプ(Cmajor、Cminorのキー)であることです。間隔(マイナー3、メジャー3)でも同じ問題が発生します。

私は、プログラム内のすべての「シンボル」の基本クラスである基本クラスであるトークンを使用しています。例えば:

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、これらすべてを同時に使用することはできません。コンテキストに依存し、これによるポリモーフィングも機能しません(どのスーパーメソッドを実行するかをどのように決定するのですか?すべてのスーパークラスコンストラクターの呼び出しはここでは発生しません)

人々が提供しなければならないデザインのアイデアや提案はありますか?オブジェクト指向の観点から音調の調和をモデリングすることに関して、グーグルで何かを見つけることができませんでした。ここのすべての概念の間には、あまりにも多くの関係があります。


8
「C」がクラスになるのはなぜですか?「Note」、「Chord」などはクラスであり、列挙型「C」が役割を果たす値列挙を持つことができます。
ロテム

ユーザーがコードCEGを入力する場合、適切なコードを形成するためにノートが何であるかを推測する必要があります。<Notes>のベクターをparamsとしてexecute()メソッドに渡すことを考えていました。これはすべて多態的に処理されます。ただし、列挙子を使用することは理にかなっていますが、使用する列挙型を使用してすべてのオブジェクトをインスタンス化する必要があります。
Igneous01

私はこれについて@Rotemと一緒にいます:時には、継承よりもオブジェクト構成を好む必要があるだけです。
スポーク

あなたがしたいのかを考えるために役立つかもしれないと私には思えるこれらのノート/コード/スケールクラスに。楽譜を作成しますか?Midiファイル?スコアの変換(移調、すべての音符の長さを2倍にする、特定の音符の上にあるすべての音符全体にトリルを追加するなど)可能なクラス構造を取得したら、それらのタスクをどのように達成するかを考えてください。気まずい場合は、別のクラス構造が必要な場合があります。
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 ++を使用すると、他の言語ほど生産的ではない場合があります。(この問題は、あなたのコードの断片は、に対処することで明らかであるtypedefvirtualデストラクタ。)プロジェクトの目標は、C ++のコードを生成することであったとしても、別の言語で最初のクラス設計を行うことが生産性かもしれません。(たとえばJavaですが、多くの選択肢があります。)

多重継承のためだけにC ++を選択しないでください。多重継承には用途がありますが、この問題をモデル化する正しい方法ではありません(音楽理論)。


明確にするために特に注意してください。英語(テキスト)の記述ではあいまいさが豊富ですが、OOPクラスを設計する際にはこれらのあいまいさを解決する必要があります。

GGシャープをノートとして話します。GメジャーGマイナーをスケールとして話します。したがって、NoteおよびScaleは交換可能な概念ではありません。が存在することができませんでした任意の同時のインスタンスすることができ、オブジェクトNoteScale

このページには、関係を示すいくつかの図が含まれています。http//www.howmusicworks.org/600/ChordScale-Relations/Chord-and-Scale-Relations

別の例として、「トライアドがあることで始まる GCメジャースケールは」「トライアドと同じ意味を持っていないと始まり CGの主要なスケール」。

この初期段階では、Tokenクラス(すべてのスーパークラス)は明確化されています。必要に応じて後で導入することができます(これがどのように役立つかを示すコードフラグメントによってサポートされます)。


まずNote、クラス図の中心であるクラスから始めNote、クラス関係図にリレーションシップ(sのタプルに関連付ける必要があるデータの断片)を徐々に追加します。

CのノートはのインスタンスであるNoteクラス。Cのメモは、関連トライアドとして、このノートに関連するプロパティ、およびその相対位置(返されIntervalに関して)Scaleその開始Cの音符。

同じクラスのインスタンス間の関係(CノートとEノートなど)は、継承ではなくプロパティとしてモデル化する必要があります。

さらに、例のクラス間の関係の多くは、プロパティとしてより適切にモデル化されています。例:

(音楽理論を再学習する必要があるため、コード例は保留中です...)


興味深い考えですが、調和解析のコンテキストでコード品質の決定をどのように処理しますか?Cコードインスタンスは、品質プロパティをマイナーに設定する必要があります(これは問題ありません)が、支配的/減少/拡張/マイナー7、9、11コードについてはどうでしょうか?単一のノートが属することができるコードはたくさんあります。コードの分析セクションにあるさまざまなタイプのコードとそれぞれの品質をどのように判断しますか?
Igneous01

私は音楽理論をほとんど知らないので、あなたの質問に答えることができません。私が理解するのに役立つ1つの方法は、これらの概念に関係するすべてのメモをリストした表を見つけることです。和音のクエリには、追加のパラメータを使用できます。
rwong

2
すべての可能な和音の非常に素晴らしいリストは次のとおりです。en.wikipedia.org / wiki / List_of_chords すべての和音は任意の音符に適用できます。Cflat major!= BMajor、ピアノでは物理的に同じ和音ですが、紙では和声の機能は非常に異なります。メモをシャープ化/フラット化する列挙子は、メモのインスタンスに最も意味があると考えています。このように、C.Sharpen()= C#、およびC.Flatten()= Cbの場合、これによりユーザーのコードを検証しやすくなります。
Igneous01

2

基本的に、音符は周波数であり、音程は周波数比です。

それに基づいて他のすべてを構築できます。

コードは、間隔のリストです。スケールは基本的な注意事項であり、チューニングシステムです。チューニングシステムは、間隔のリストでもあります。

それらの名前の付け方は、単なる文化的な産物です。

ウィキペディアの音楽理論の記事は良い出発点です。


興味深いのですが、基礎となる物理的現実に関してシステムをモデル化することが必ずしも役立つかどうかはわかりませんが。モデルは特定の側面を明確にするのに役立つことが必要であり、必ずしも包括的または正確である必要はありません。アプローチは正確かつ包括的ですが、OPのユースケースには低すぎる可能性があります。
コンラッドルドルフ

@KonradRudolph-私の極端な立場で、夏時間と同様に、基礎となるモデルをプレゼンテーションレイヤーと混ぜてはならないことを指摘したかっただけです。モデル自体の計算ははるかに簡単です。最も役立つ抽象化レベルは私が提案したものではないことに同意しますが、OPによって提案された抽象化レベルも適切ではないと感じます。
ムーヴィシエル

このプログラムの目的は、必ずしも音楽の物理的な現実を表示することではありません。しかし、理論(私のような)を勉強している人にとっては、いくつかの和音をすばやくプロットし、これらの和音が調和的な意味で互いにどのように関連しているかをプログラムに最大限に解釈させることができます。スコアをメジャーごとに読み取るための試行錯誤された真の方法で分析することができましたが、これは物事を簡単にし、分析する際により細かい詳細に焦点を当てることができる別のツールです。
Igneous01

1

この議論は魅力的です。

メモはMIDI(またはある種のトーンキャプチャデバイス)を介して入力されていますか、それとも文字や記号を入力して入力されていますか?

CからD-シャープ/ E-フラットまでの間隔の場合:

D-シャープとE-フラットは同じトーン(A = 440Hzの場合、約311Hz)ですが、C-> D-シャープからの間隔は2番目に増補され、C-> E-フラットからの間隔はマイナー3。メモの書き方がわかれば十分簡単です。続行する2つのトーンだけがあるかどうかを判断することはできません。

この場合、.SemiToneUp()、. FullToneDown()など、前述の.Sharpen()および.Flatten()メソッドとともにトーンをインクリメント/デクリメントする方法も必要になると思います。シャープ/フラットとして「色付け」することなく、スケール内の次の音を見つけることができます。

「C」はそれ自体がクラスではなく、Noteクラスのインスタンス化であることに@Rotemに同意する必要があります。

すべての音程を半音として含む音のプロパティを定義すると、初期音の値( "C"、 "F"、 "G#")に関係なく、3つの音のシーケンスがルート、メジャー3番目(M3)、マイナー3番目(m3)はメジャートライアドになります。同様に、m3 + M3はマイナートライアドであり、m3 + m3は減少し、M3 + M3は増加します。さらに、これにより、12のベースノートすべてとそれらのオクターブを上下に明示的にコーディングせずに、11番目、13番目の減少などを検出する方法をカプセル化できます。

それが完了しても、解決すべきいくつかの問題が残っています。

トライアドC、E、Gを取る。ミュージシャンとして、私はこれをCmaj和音として明確に見ています。ただし、私の開発者は、この追加をEマイナー拡張5(ルートE + m3 + a5)またはGsus4 6th no 5th(RootG + 4 + 6)として解釈できます。

したがって、分析の実行に関するあなたの質問に答えるには、モダリティ(maj、minorなど)を決定する最良の方法は、入力されたすべての音符を取得し、昇順の半音値に配置し、既知の和音形式に対してテストすることだと思います。次に、ルートノートとして入力した各ノートを使用して、同じ評価セットを実行します。

コード形式に重みを付けて、より一般的な(メジャー、マイナー)コードが拡張、中断、エレクトラなどのコード形式よりも優先されるようにできますが、正確な分析には、一致するすべてのコード形式を可能な解決策として提示する必要があります。

繰り返しになりますが、参照されているウィキペディアの記事はピッチクラスを一覧表示するのに適しています。そのため、コードのモデルをコーディングし、入力したノートを取得し、ピッチクラス/間隔に割り当ててから比較するのは簡単です(退屈ではありますが)一致する既知の形式に対して。

これはとても楽しかったです。ありがとう!


今のところ、テキストを介して入力されています。ただし、プログラムが適切にカプセル化されていれば、後でMIDIを使用できる場合があります。今赤ちゃんの手順:D
Igneous01

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))

これで、解釈された間隔と2つの間の物理的距離の両方がわかりました。そのため、間隔名はすでに半分完全です。

これで間隔を解釈できるようになりました。間隔が5で、距離が8の場合、5番目の拡張です。

これまでは音符と音程が期待どおりに機能していましたが、今ではコード識別子に取り組むだけです。

再度感謝します。これらの回答の一部を読み直し、ここにいくつかのアイデアを組み込みます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.