OO设计,如何为音调和声建模?


12

我已经开始用C ++ 11编写一个程序来分析和弦,音阶和和声。我在设计阶段遇到的最大问题是,音符“ C”是音符,和弦类型(Cmaj,Cmin,C7等)和键类型(Cmajor,Cminor的键)。间隔也会出现相同的问题(小三,大三)。

我使用的是基类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','Chord'等是类,它们可以具有值枚举,枚举'C'可能在其中起作用。
Rotem

如果用户输入->和弦CEG,则需要推断出要形成适当和弦的音符。我当时想将<Notes>的向量作为参数传递给execute()方法,该方法将全部以多态处理。但是,使用枚举数是有意义的,但是随后我将需要使用要使用的枚举实例化每个对象。
Igneous01

我在这上面使用@Rotem:有时,您只需要对象组合而不是继承。
Spoike

在我看来,这可能是考虑你想要什么有帮助这些音符/和弦/规模类。您要制作乐谱吗?MIDI文件?是否对乐谱进行转换(换位,将所有音符长度加倍,对某个音符之上的所有整个音符添加颤音等)?一旦有了可能的类结构,请考虑如何完成这些任务。如果看起来很尴尬,也许您想要一个不同的类结构。
MatrixFrog

Answers:


9

我认为最好的方法是重现这些实体之间的真实关系。

例如,您可能有:

  • 一个Note对象,其属性是

    • 名称(C,D,E,F,G,A,B)

    • 偶然的(自然的,平坦的,尖锐的)

    • 频率或另一个唯一的音高标识符

  • 一个Chord对象,其属性是

    • 一组Note对象

    • 名称

    • 偶然

    • 质量(主要,次要,减弱,增强,暂停)

    • 加法(7、7 +,6、9、9 +,4)

  • 一个Scale对象,其属性是

    • 一组Note对象

    • 名称

    • 类型(大调,自然小调,旋律小调,和声小调)

    • 模式(爱奥尼亚语,多利安语,phrygian,lydian,mixolidian,风神,locrian)

然后,如果您输入的是文本,则可以创建一个带有字符串的注释,其中包括注释名称,意外和(如果需要)八度音阶。

例如(伪代码,我不懂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 Sharp称为笔记。我们讲的G大调G小调为尺度。因此,NoteScale不是可互换的概念。不能有任何对象可以同时是a Note和a 的实例Scale

此页面包含一些说明这种关系的图表:http : //www.howmusicworks.org/600/ChordScale-Relations/Chord-and-Scale-Relations

再如,“三合会即与开始 C大调规模”并不具有相同的含义为“三合会即与启动 çG大的规模”。

在这个早期阶段,Token该类(一切的超类)是不需要的,因为它可以防止歧义。如果需要,可以稍后引入(由代码片段支持,该代码片段演示了如何使用此功能)。


首先,从Note作为类图中心的类开始,然后将关系(需要与Notes 元组关联的数据段)逐渐添加到类关系图中。

一个ç说明是实例Note类。甲Ç音符将返回与此相关的笔记,如相关的三元组属性,以及其相对位置(Interval相对于)到Scale开始Ç音符。

同一类实例之间的关系(例如,C音符和E音符之间)的关系应建模为属性,而不是继承。

此外,示例中的许多类间关系也更恰当地建模为属性。例:

(代码示例待定,因为我需要重新学习音乐理论...)


有趣的想法,但是在和声分析的背景下如何处理和弦质量呢?C和弦实例需要具有品质属性,将其设置为次要(没关系),但是占主导地位的/减弱的/增强的/未成年人的7s,9、11和弦又如何呢?单个音符可以属于很多和弦。如何确定代码的分析部分中的各种类型的和弦及其各自的品质?
Igneous01

我对音乐理论了解很少,所以我无法回答您的问题。可以帮助我理解的一种方法是找到一张表,其中列出了这些概念中涉及的所有注释。对和弦的查询可以采用其他参数。
rwong

2
这是所有可能的和弦的一个非常不错的列表:en.wikipedia.org/wiki/List_of_chords 所有和弦都可以应用于任何音符,对我而言,重要的是,和声是正确的:即。Cflat major!= BMajor,它们在钢琴上实际上是相同的和弦,但在纸上它们的和声功能却大不相同。我认为,对一个音符实例进行枚举/展平的枚举器最有意义。通过C.Sharpen()= C#和C.Flatten()= Cb的方式,这可能使我更容易验证用户的和弦。
Igneous01

2

基本上,音符是频率,音程是频率比。

其他一切都可以基于此。

和弦是音程列表。音阶是基本音符和调音系统。调音系统也是间隔列表。

您如何命名它们只是文化手工艺品。

维基百科的音乐理论文章是一个不错的起点。


有趣的是,尽管我不确定根据基本物理现实对系统进行建模是否必定会有所帮助。请记住,该模型必须有助于阐明一个特定方面,而不必一定是全面甚至准确的。尽管您的方法既准确又全面,但是对于OP的用例来说可能太低了。
康拉德·鲁道夫2013年

@KonradRudolph-就我的极端立场而言,我只是想指出,不应以类似于夏令时的方式将基础模型与表示层混合:在模型本身上的计算要容易得多。我同意,最有用的抽象级别不是我建议的,但是我认为OP建议的抽象级别也不足够。
mouviciel 2013年

该程序的目的不一定是显示音乐的真实感。但是对于学习理论的人(例如我自己)来说,他们可以快速绘制某些和弦,并让程序以其最佳的能力来解释这些和弦如何在和声上相互关联。我可以分析它是一种按量度读取分数的可靠且可靠的方法,但这是使事情变得更容易并且能够在分析时专注于更精细细节的另一种工具。
Igneous01 2013年

1

我发现这种引人入胜的魅力。

是通过midi(或某种类型的声音捕获设备)输入音符,还是通过键入字母和符号输入音符?

对于从C到D-sharp / E-flat的间隔:

尽管D-sharp和E-flat的音调相同(如果A = 440Hz,则约为311Hz),但将C-> D-sharp的间隔写为增强的2nd,而将C-> E-flat的间隔写为a。小三级 如果您知道笔记是如何写的,就很容易。无法确定是否只有两个音调可以继续。

在这种情况下,我相信您还需要一种增加/减少音调的方法以及上述的.Sharpen()和.Flatten()方法,例如.SemiToneUp(),.FullToneDown()等。您可以按比例查找后续音符,而无需将它们“着色”为尖锐/平整。

我必须同意@Rotem的观点,“ C”本身并不是一个类,而是Note类的实例化。

如果您定义一个音符的属性,包括所有音程为半音,则不管初始音符值(“ C”,“ F”,“ G#”)如何,您都可以分辨出三个音符序列具有根,大三级(M3),然后小三级(m3)将成为大三合会。同样,m3 + M3是次要三元组,m3 + m3减小,M3 + M3增强。此外,这将为您提供一种封装,以找到第11个,减少的第13个等等,而无需为所有12个基音及其上下八度音程进行明确编码。

完成后,您仍然需要解决一些问题。

以三合会C,E,G。作为音乐家,我清楚地将其视为Cmaj和弦。但是,我中的开发人员可以将此额外内容解释为E小扩充5(Root E + m3 + a5)或Gsus4 6th no 5th(RootG + 4 + 6)。

因此,为了回答您进行分析的问题,我认为确定模态(最佳,次要等)的最佳方法是将所有输入的音符都记录下来,以升半音值的形式排列它们,并根据已知的和弦形式对其进行测试。 。然后,将输入的每个音符用作根音符,并执行相同的评估。

您可以对和弦形式进行加权,以使更常见(大,小)的优先级高于增强,悬浮,弯音等和弦形式,但是要进行准确的分析,则需要提供所有匹配的和弦形式作为可能的解决方案。

同样,所引用的Wikipedia文章很好地列出了音高类别,因此应该很简单(尽管很乏味)来编码和弦模型,记录输入的音符,将它们分配给音高类别/音程,然后进行比较对照已知的比赛形式。

这很有趣。谢谢!


目前正在通过文本输入它们。但是稍后,如果程序被正确封装,我也许可以使用midi。宝宝现在走了:D
Igneous01

0

听起来像是模板的情况。你似乎有一个template <?> class Major : public Chord;这样Major<C>的-A 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,则它是增加的5th。

到目前为止,音符和音符按预期工作,现在我只需要解决和弦标识符。

再次感谢,我将重读其中的一些答复,并在此处合并一些想法。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.