web-dev-qa-db-ja.com

OO設計、色調調和のモデル化方法は?

和音、音階、和声を分析するプログラムを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は、これらすべてを同時に使用することはできません。これは状況依存であり、これによるポリモーフィングは機能しません(どのスーパーメソッドを実行するかを決定する方法?すべてのスーパークラスコンストラクターの呼び出しはここでは発生しないはずです)

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

12
Igneous01

最善のアプローチは、これらのエンティティ間の実際の関係を再現することです。

たとえば、次のようにすることができます。

  • プロパティがNoteオブジェクト

    • 名前(C、D、E、F、G、A、B)

    • 偶発的(自然、フラット、シャープ)

    • 周波数または他の一意のピッチ識別子

  • プロパティがChordオブジェクト

    • Noteオブジェクトの配列

    • 名前

    • 偶然

    • 品質(メジャー、マイナー、ディミニー、オーグメント、サスペンド)

    • 追加(7、7 +、6、9、9 +、4)

  • プロパティがScaleオブジェクト

    • Noteオブジェクトの配列

    • 名前

    • タイプ(メジャー、ナチュラルマイナー、メロディックマイナー、ハーモニックマイナー)

    • モード(ionian、dorian、phrygian、lydian、mixolidian、aeolian、locrian)

次に、入力がテキストの場合、ノート名、偶発的、および(必要な場合)オクターブを含む文字列でノートを作成できます。

例(疑似コード、C++は知りません):

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

次に、Noteクラスで文字列を解析し、プロパティを設定できます。

Chordはそのノートによって構築できます:

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

...または名前、品質、追加のメモを含む文字列で:

chord = new Chord('Cmaj7');

私はあなたのアプリケーションが正確に何をするかわかりませんので、これらは単なるアイデアです。

あなたの魅力的なプロジェクトで頑張ってください!

9
lortabac

いくつかの一般的なアドバイス


クラスの設計に予期される多くの不確実性がある場合(状況など)、異なる競合するクラスの設計で実験することをお勧めします。

この段階でC++を使用することは、他の言語ほど生産的でない場合があります。 (この問題は、typedefおよびvirtualデストラクタを処理する必要のあるコードフラグメントで明らかです。)プロジェクトの目標がC++コードの作成である場合でも、最初のクラス設計を他の言語。 (たとえば、Javaですが、選択肢はたくさんあります。)

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


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

[〜#〜] g [〜#〜]Gシャープノートとして。 GメジャーおよびGマイナーをスケールと呼びます。したがって、NoteScaleは互換性のある概念ではありません。 NoteScaleのインスタンスを同時に使用できるanyオブジェクトはありません。

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

別の例として、「で始まるトライアド[〜#〜] g [〜#〜]Cメジャースケール)は「で始まるトライアドと同じ意味ではありません」 [〜#〜] c [〜#〜]Gメジャースケール」。

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


まず、クラス図の中心であるNoteクラスから始め、クラスに関係(Notesのタプルに関連付ける必要のあるデータ)を徐々に追加します関係図。

[〜#〜] c [〜#〜]ノートはNoteクラスのインスタンスです。 [〜#〜] c [〜#〜]ノートは、関連するトライアドやその相対位置など、このノートに関連するプロパティを返します(Interval)に関してScaleに関してで始まります[〜#〜] c [ 〜#〜]注。

同じクラスのインスタンス間の関係(たとえば、[〜#〜] c [〜#〜]ノートと[〜#〜] e [〜#〜]注)継承ではなく、プロパティとしてモデル化する必要があります。

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

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

4
rwong

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

それ以外はすべてその上に構築できます。

和音は間隔のリストです。スケールは基本的な音であり、チューニングシステムです。チューニングシステムも間隔のリストです。

それらにどのように名前を付けるかは、単に文化的な工芸品です。

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

2
mouviciel

私はこの議論が魅力的だと思っています。

ノートはmidi(または何らかのタイプのトーンキャプチャデバイス)経由で入力されていますか、それとも文字や記号を入力して入力されていますか?

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

DシャープとEフラットは同じトーン(A = 440Hzの場合は約311Hz)ですが、C-> Dシャープからの間隔は2倍に書き込まれますが、C-> Eフラットからの間隔はマイナー3位。あなたがメモが書かれた方法を知っていれば十分簡単です。続行する2つのトーンしかないかどうかを判断することはできません。

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

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

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

それが終わっても、解決すべき問題が残っています。

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

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

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

ここでも、参照されているウィキペディアの記事はピッチクラスを一覧表示するのに適しているため、コードのモデルをコード化し、入力されたノートを取り、ピッチクラス/間隔に割り当てて比較するのは簡単です(面倒です)。マッチの既知のフォームに対して。

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

1
John

すべての提案をありがとう、どういうわけか私は余分な応答を逃すことができました。これまでのところ、私のクラスは次のように設計されています。

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

間隔とコード計算の問題を解決するために、循環バッファーを使用することにしました。これにより、一致する次の音符が見つかるまで、バッファーを任意のポイントからトラバースできます。

解釈された間隔を見つけるために、実際のノートバッファーをトラバースするには、文字が一致するときに停止します(実際のノートや位置ではなく、文字だけが一致する)。

12整数の別のバッファーを実際の距離トラバースを見つけるには、トップノートの位置がインデックスでのバッファーの値と同じであるときに停止します。これも前に進むだけです。ただし、オフセットはどこでもかまいません(つまり、buffer.at(-10))。

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

今、私は間隔を解釈することができます、すなわち。間隔が5で距離が8の場合は、5倍になります。

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

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

0
Igneous01

テンプレートの場合のように聞こえます。 template <?> class Major : public Chord; そう Major<C> is-a Chord、そのままMajor<B>。同様に、Note<?>インスタンス付きテンプレートNote<C>およびNote<D>

私が除外した唯一のものは?部分。 enum {A,B,C,D,E,F,G}しかし、その列挙型の名前をどのように付けるかわかりません。

0
MSalters