空客的音乐笔记

“乐”作为六艺之一,是日常生活的重要组成部分;乐府部也是你社的核心部门之一 :smirking_face: 所以空客开一个楼聊一聊音乐,尽量提取西方乐理和中国传统音乐的共同点,中西结合一下,而且尝试能讲得比你交的《中西乐理及其应用》更深入、更有用。建楼的初衷是音乐风格赏析,不过有些基础知识可能必须要先讲。

一、音高与律制

TL;DR: 在十二平均律下,任意频率是 2:1 的两个构成一个八度;将这两个音按照比例十二等分,得到十二平均律的一个音阶,相邻两个音之间构成一个半音,频率之比为 \sqrt[12]{2}:1

1. 音高

音高的物理实质是频率。 当我们的耳朵告诉我们,一个音比另一个音高,其实是这个音的频率比另一个音的频率在数值上更大。这里有几个隐藏假设:

  1. 单位性:一个“音”的主频率是一个固定的数值;暂不考虑连音、装饰音以及其他花里胡哨的音
  2. 距离性:一首音乐中出现的音按频率排序,则相邻两个音离得不能太近,需要便于比较(换种理解就是,如果真的出现了离得很近的两个音,那听者会觉得其实是一个音)
  3. 平移性:将一首歌的所有音的频率乘上一个相同的实数(不能离1太远),则这首歌只被“移调”,根本性质没有发生改变
运行以下代码可以听到随机生成20个等时值音高的播放效果
# 需要在Windows上运行
from ctypes import windll
from random import randint

Beep = windll.kernel32.Beep

for _ in range(20):
    Beep(randint(100, 1000), 200) # 一般认为人类可以听见的频率范围是20~20000,这里取个稳妥范围

2. 音程

假设2非常强,而且符合奥卡姆剃刀原理(如无必要、勿增实体),所以世界各地都在尝试把可以使用的音高放进一个相对有限的集合,那这种方法就是律制——决定了音乐音高的取值范围。不过,律制的确定需要引入“音程”的概念。

两个音高的“音程”表示它们之间的频率比值。人类发现了一个现象:这个比值(分数)越简单,两个音的组合倾向于越自然。最简单的比值是 2:1 ,如此的两个音直接构成一个 (纯)八度。除此之外的自然比值有:

比值 称呼
5:3 大六度
3:2 (纯)五度
4:3 (纯)四度
5:4 大三度
运行以下代码,听一听这些自然的音程
from ctypes import windll
Beep = windll.kernel32.Beep

Beep(330, 1000), Beep(660, 2000) # 八度
Beep(330, 1000), Beep(550, 2000) # 大六度
Beep(440, 1000), Beep(660, 2000) # 五度
Beep(330, 1000), Beep(440, 2000) # 四度
Beep(440, 1000), Beep(550, 2000) # 大三度
什么叫“度”?

这是个历史遗留问题(屎山),而且涉及“先有鸡还是先有蛋”(先有音程概念还是先有律制)的问题,一点都不直观,但是既成术语,我们必须得用。大致的意思就是,在钢琴中,两个白键之间隔了多少个白键(取一个端点),就是多少“度”。一个音和自己构成“(纯)一度”;一个音和比它高一个八度的音构成“八度”(废话)。

3. 律制

如果严格按照那些比例,则一个八度内要有 3\times4\times5=60 个音——人耳根本分辨不出来。东西方律制经历各种方法,最后改良成十二平均律——将一个八度等分成12份,设 p=2^\frac{1}{12},则 5:3\approx p^9, 3:2\approx p^7, 4:3\approx p^5, 5:4\approx p^4,用一种近似的方法来处理“自然”问题。

运行以下代码,听一听十二平均律下的这些音程
from ctypes import windll
Beep = windll.kernel32.Beep

Beep(330, 1000), Beep(660, 2000) # 八度,这里本来就没变
Beep(330, 1000), Beep(555, 2000) # 大六度,这里从550变成了555
Beep(440, 1000), Beep(660, 2000) # 五度,这里四舍五入后没变
Beep(330, 1000), Beep(440, 2000) # 四度,这里四舍五入后没变
Beep(440, 1000), Beep(555, 2000) # 大三度,这里从550变成了555

可以发现,四度和五度的取舍非常少,因此十二平均律还是比较健壮的。

如此,我们称频率之比为 p 的两个音构成一个“半音”,之比为 p^2 的两个音构成一个“全音”;则一个八度=12个半音。

把“度”语言翻译成“半音”语言
几个半音
(纯)一度 0
小二度 1
大二度 2
小三度 3
大三度 4
(纯)四度 5
增四度/减五度 6
(纯)五度 7
小六度 8
大六度 9
小七度 10
大七度 11
(纯)八度 12
小九度 13

为什么“增四度/减五度”那么奇怪呢?因为它值得那么奇怪——6个半音,频率比值是 \sqrt{2}:1,在十二平均律中最像无理数的无理数,连分数展开是 3:2, 7:5, 17:12, 41:29, ... 非常不自然。

确定了音高的相对位置,自然也就能确定绝对位置,每种音高定一个名字,称为音名,相差整数个八度的音高音名相同而阶数不同。

音名 阶数 频率(Hz)
A 3 220
#A/bB 3
B 3
C 4
#C/bD 4
D 4
#D/bE 4
E 4
F 4
#F/bG 4
G 4
#G/bA 4
A 4 440
#A/bB 4
B 4
C 5
#C/bD 5
D 5
#D/bE 5
E 5
F 5
#F/bG 5
G 5
#G/bA 5
A 5 880

其中“#”、“b”分别表示“升(一个半音)”、“降(一个半音)”。

音名主体有CDEFGAB七个(钢琴白键),另外5个带升降号的(钢琴黑键)。

第一章可能比较枯燥,而且和音乐风格没啥大关系;下一章打算讲“调式”,就可以引入一点音乐风格的介绍了。

顺便给近作引个流:
毕业歌1:当越剧遇上民谣_哔哩哔哩_bilibili

1 个赞

我第一个前来支持

cy

二、基本调式

TL;DR: 自然大调指的是1234567,自然小调指的是6712345,旋律小调是自然小调的4、5可能升半音成#4、#5形成的调式。

0. 音名与唱名

嗯,之前说过音名的主体是CDEFGAB七个,它们之前的关系是差2、2、1、2、2、2、1个半音;那么同样是七个的,我们熟知的另一套术语,是 do re mi fa sol la si,或者写成数字的1234567或者罗马数字的I II III IV V VI VII,名字叫“唱名”,两套术语之间的对应关系是什么呢?

不同的著作可能有不同的说法,但是在我这里规定为:

  1. CDEFGAB(包括其升降形式)是音名,表示的是绝对音高
  2. 1234567(包括其升降形式)是唱名,表示的是相对音高

音名一般用于实际演奏,唱名用于演唱或分析。在不同的调性(移调转调)中,1可能对应不同的音名,比如在C自然大调中,1234567对应CDEFGAB;在B自然大调中,1234567对应B#C#DE#F#G#A。

1. 自然大调

自然大调就是由“差2、2、1、2、2、2、1个半音”的音构成、且认为“八度”是一个周期的调式,写成唱名就是1234567,就是钢琴的白键。它的“自然”更多地体现在历史传承上而不是物理上的自然。绝大多数调式都可以认为是自然大调的变体。有一个隐藏假设有必要提一下:

  1. “八度”周期。指的是,从1234567往上或往下延伸(解析延拓),还是 \dot1\dot2\dot3\dot4\dot5\dot6\dot7\underset{\bullet}1\underset{\bullet}2\underset{\bullet}3\underset{\bullet}4\underset{\bullet}5\underset{\bullet}6\underset{\bullet}7 ,以八度为一个周期。这个性质在大多数调式都是适用的,但是也有一些奇怪的调式并非如此,比如以五度为一个周期。

让我们用更好听的方式来听一听示例:

自然大调示例:小红帽(节选)
from ctypes import windll, c_longlong, pointer
from enum import Enum

kernel32 = windll.kernel32
Sleep = kernel32.Sleep
QueryPerformanceCounter = kernel32.QueryPerformanceCounter
QueryPerformanceFrequency = kernel32.QueryPerformanceFrequency
winmm = windll.winmm
midiOutOpen = winmm.midiOutOpen
midiOutClose = winmm.midiOutClose
midiOutShortMsg = winmm.midiOutShortMsg

class Notes(Enum):
    C1 = 24
    bD1 = 25
    D1 = 26
    bE1 = 27
    E1 = 28
    F1 = 29
    bG1 = 30
    G1 = 31
    bA1 = 32
    A1 = 33
    bB1 = 34
    B1 = 35
    C2 = 36
    bD2 = 37
    D2 = 38
    bE2 = 39
    E2 = 40
    F2 = 41
    bG2 = 42
    G2 = 43
    bA2 = 44
    A2 = 45
    bB2 = 46
    B2 = 47
    C3 = 48
    bD3 = 49
    D3 = 50
    bE3 = 51
    E3 = 52
    F3 = 53
    bG3 = 54
    G3 = 55
    bA3 = 56
    A3 = 57
    bB3 = 58
    B3 = 59
    C4 = 60
    bD4 = 61
    D4 = 62
    bE4 = 63
    E4 = 64
    F4 = 65
    bG4 = 66
    G4 = 67
    bA4 = 68
    A4 = 69
    bB4 = 70
    B4 = 71
    C5 = 72
    bD5 = 73
    D5 = 74
    bE5 = 75
    E5 = 76
    F5 = 77
    bG5 = 78
    G5 = 79
    bA5 = 80
    A5 = 81
    bB5 = 82
    B5 = 83
    C6 = 84
    bD6 = 85
    D6 = 86
    bE6 = 87
    E6 = 88
    F6 = 89
    bG6 = 90
    G6 = 91
    bA6 = 92
    A6 = 93
    bB6 = 94
    B6 = 95

class Velocities(Enum):
    pppp = 8
    ppp = 20
    pp = 31
    p = 42
    mp = 53
    mf = 64
    f = 80
    ff = 96
    fff = 112
    ffff = 127

class Instrument(Enum):
    AcousticGrandPiano = 0
    BrightAcousticPiano = 1
    ElectricGrandPiano = 2
    HonkyTonkPiano = 3
    RhodesPiano = 4
    ChorusedPiano = 5
    Harpsichord = 6
    Clavinet = 7
    Celesta = 8
    Glockenspiel = 9
    MusicBox = 10
    Vibraphone = 11
    Marimba = 12
    Xylophone = 13
    TubularBells = 14
    Dulcimer = 15
    DrawbarOrgan = 16
    PercussiveOrgan = 17
    RockOrgan = 18
    ChurchOrgan = 19
    ReedOrgan = 20
    Accordion = 21
    Harmonica = 22
    TangoAccordion = 23
    AcousticGuitarNylon = 24
    AcousticGuitarSteel = 25
    ElectricGuitarJazz = 26
    ElectricGuitarClean = 27
    ElectricGuitarMuted = 28
    OverdrivenGuitar = 29
    DistortionGuitar = 30
    GuitarHarmonics = 31
    AcousticBass = 32
    ElectricBassFinger = 33
    ElectricBassPick = 34
    FretlessBass = 35
    SlapBass1 = 36
    SlapBass2 = 37
    SynthBass1 = 38
    SynthBass2 = 39
    Violin = 40
    Viola = 41
    Cello = 42
    Contrabass = 43
    TremoloStrings = 44
    PizzicatoStrings = 45
    OrchestralHarp = 46
    Timpani = 47
    StringEnsemble1 = 48
    StringEnsemble2 = 49
    SynthStrings1 = 50
    SynthStrings2 = 51
    ChoirAahs = 52
    VoiceOohs = 53
    SynthVoice = 54
    OrchestraHit = 55
    Trumpet = 56
    Trombone = 57
    Tuba = 58
    MutedTrumpet = 59
    FrenchHorn = 60
    BrassSection = 61
    SynthBrass1 = 62
    SynthBrass2 = 63
    SopranoSax = 64
    AltoSax = 65
    TenorSax = 66
    BaritoneSax = 67
    Oboe = 68
    EnglishHorn = 69
    Bassoon = 70
    Clarinet = 71
    Piccolo = 72
    Flute = 73
    Recorder = 74
    PanFlute = 75
    BlownBottle = 76
    Shakuhachi = 77
    Whistle = 78
    Ocarina = 79
    Lead1Square = 80
    Lead2Sawtooth = 81
    Lead3Calliope = 82
    Lead4Chiff = 83
    Lead5Charang = 84
    Lead6Voice = 85
    Lead7Fifths = 86
    Lead8BassLead = 87
    Pad1NewAge = 88
    Pad2Warm = 89
    Pad3Polysynth = 90
    Pad5Bowed = 92
    Pad6Metallic = 93
    Pad7Halo = 94
    Pad8Sweep = 95
    FX1Rain = 96
    FX2Soundtrack = 97
    FX3Crystal = 98
    FX4Atmosphere = 99
    FX5Brightness = 100
    FX6Goblins = 101
    FX7Echoes = 102
    FX8SciFi = 103
    Sitar = 104
    Banjo = 105
    Shamisen = 106
    Koto = 107
    Kalimba = 108
    Bagpipe = 109
    Fiddle = 110
    Shanai = 111
    TinkleBell = 112
    Agogo = 113
    SteelDrums = 114
    Woodblock = 115
    TaikoDrum = 116
    MelodicTom = 117
    SynthDrum = 118
    ReverseCymbal = 119
    GuitarFretNoise = 120
    BreathNoise = 121
    Seashore = 122
    BirdTweet = 123
    TelephoneRing = 124
    Helicopter = 125
    Applause = 126
    Gunshot = 127


class Player:
    def __init__(self, tracks, tempo):
        val = c_longlong()
        QueryPerformanceFrequency(pointer(val))
        self.frequency = 1000 / val.value
        self.hmo = c_longlong()
        midiOutOpen(pointer(self.hmo), 0, 0, 0, 0)
        for i, instrument in enumerate(tracks):
            self.ChangeInstrument(i, instrument.value)
        self.tick = 60000 / tempo
        self.stamp = 0
        self.total_tick = 0

    def timestamp(self) -> float:
        val = c_longlong()
        QueryPerformanceCounter(pointer(val))
        return val.value

    def reset_timer_wait(self):
        self.total_tick = 0
        self.stamp = self.timestamp()
        
    def timer_wait(self, d):
        self.total_tick += d
        t = d - 20
        if t > 0:
            Sleep(int(t))
        while (self.timestamp() - self.stamp) * self.frequency < self.total_tick:
            pass

    def close(self):
        midiOutClose(self.hmo)
        
    def SendMidiEvent(self, bStatus, bData1, bData2):
        return midiOutShortMsg(self.hmo, bStatus | (bData1 << 8) | (bData2 << 16))
    
    def NoteOn(self, track, note, velocity):
        return self.SendMidiEvent(0x90 | track, note, velocity)

    def NoteOff(self, track, note):
        return self.SendMidiEvent(0x80 | track, note, 0)

    def ChangeInstrument(self, track, instrument):
        return self.SendMidiEvent(0xC0 | track, instrument, 0)
    
    def play(self, records):
        self.reset_timer_wait()
        notes = []
        for record in records:
            note = record[0]
            if note == 0: # proceed
                val = record[2] + (record[3] << 8)
                while val != 0:
                    min_time = val
                    for rec in notes:
                        if rec[2] < min_time:
                            min_time = rec[2]
                    self.timer_wait(self.tick * min_time)
                    val -= min_time
                    new_notes = []
                    for rec in notes:
                        rec[2] -= min_time
                        if rec[2] == 0:
                            self.NoteOff(rec[1], rec[0].value)
                        else:
                            new_notes.append(rec)
                    notes = new_notes
            else:
                notes.append(record)
                self.NoteOn(record[1], note.value, record[3].value)
            

def proceed(d):
    return [0, 0, d, 0]


if __name__ == '__main__':
    tempo = 240
    tracks = [
        Instrument.AcousticGuitarSteel,
    ]
    player = Player(tracks, tempo)
    player.play([
        [Notes.C4, 0, 1, Velocities.ff],
        proceed(1),
        [Notes.D4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.E4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.F4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.G4, 0, 2, Velocities.ff],
        proceed(2),
        [Notes.E4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.C4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.C5, 0, 2, Velocities.ff],
        proceed(2),
        [Notes.A4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.F4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.G4, 0, 1, Velocities.ff],
        proceed(1),
        [Notes.G4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.E4, 0, 2, Velocities.f],
        proceed(2),
        
        [Notes.C4, 0, 1, Velocities.ff],
        proceed(1),
        [Notes.D4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.E4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.F4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.G4, 0, 1, Velocities.ff],
        proceed(1),
        [Notes.E4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.D4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.C4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.D4, 0, 2, Velocities.ff],
        proceed(2),
        [Notes.E4, 0, 2, Velocities.f],
        proceed(2),
        [Notes.D4, 0, 2, Velocities.ff],
        proceed(2),
        [Notes.G4, 0, 2, Velocities.f],
        proceed(2),
        
        [Notes.C4, 0, 1, Velocities.ff],
        proceed(1),
        [Notes.D4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.E4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.F4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.G4, 0, 2, Velocities.ff],
        proceed(2),
        [Notes.E4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.C4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.C5, 0, 2, Velocities.ff],
        proceed(2),
        [Notes.A4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.F4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.G4, 0, 2, Velocities.ff],
        proceed(2),
        [Notes.E4, 0, 2, Velocities.f],
        proceed(2),
        
        [Notes.C4, 0, 1, Velocities.ff],
        proceed(1),
        [Notes.D4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.E4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.F4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.G4, 0, 1, Velocities.ff],
        proceed(1),
        [Notes.E4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.D4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.C4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.D4, 0, 2, Velocities.ff],
        proceed(2),
        [Notes.E4, 0, 2, Velocities.f],
        proceed(2),
        [Notes.C4, 0, 2, Velocities.ff],
        proceed(2),
        [Notes.C4, 0, 2, Velocities.f],
        proceed(2),
    ])
    Sleep(1000)
    player.close()

传统观点认为,在满足用音为1234567的前提下,自然大调的乐曲的特征有(从严格到宽松):

  1. 主旋律的音以1开始、以1结尾;上面的《小红帽》就是一个典型的例子
  2. 主旋律的音不一定以1开始,但是以1结尾;典型的例子是4536251和弦进行的歌曲,之后会细讲
  3. 主旋律的音不一定以1开始或结尾,但是主旋律与1关系密切,这时候可能没有直接的套路来判断;可能的例子是,一首歌没有结尾(比如选择用循环渐弱的方式结尾)

新兴观点会之后讲。

传统观点认为自然大调乐曲的特征是“具有光辉,明朗的色彩”。空客在此指出,音乐风格是综合特征,不应机械地理解;而“调式”是一个非常灵活的量,最多只能确定一个大方向,具体的判断仍需自底而上地分析。

2. 自然小调

自然小调可以认为是自然大调的最常见的变体,由“差2、1、2、2、1、2、2个半音”的音阶构成,同样认为“八度”是一个周期,写成唱名是6712345。

可以发现,自然小调就是以自然大调的6开头的调式,其音高构成的集合与自然大调完全相同,只不过主音不同。

自然小调示例:青春舞曲(节选)
from ctypes import windll, c_longlong, pointer
from enum import Enum

kernel32 = windll.kernel32
Sleep = kernel32.Sleep
QueryPerformanceCounter = kernel32.QueryPerformanceCounter
QueryPerformanceFrequency = kernel32.QueryPerformanceFrequency
winmm = windll.winmm
midiOutOpen = winmm.midiOutOpen
midiOutClose = winmm.midiOutClose
midiOutShortMsg = winmm.midiOutShortMsg

class Notes(Enum):
    C1 = 24
    bD1 = 25
    D1 = 26
    bE1 = 27
    E1 = 28
    F1 = 29
    bG1 = 30
    G1 = 31
    bA1 = 32
    A1 = 33
    bB1 = 34
    B1 = 35
    C2 = 36
    bD2 = 37
    D2 = 38
    bE2 = 39
    E2 = 40
    F2 = 41
    bG2 = 42
    G2 = 43
    bA2 = 44
    A2 = 45
    bB2 = 46
    B2 = 47
    C3 = 48
    bD3 = 49
    D3 = 50
    bE3 = 51
    E3 = 52
    F3 = 53
    bG3 = 54
    G3 = 55
    bA3 = 56
    A3 = 57
    bB3 = 58
    B3 = 59
    C4 = 60
    bD4 = 61
    D4 = 62
    bE4 = 63
    E4 = 64
    F4 = 65
    bG4 = 66
    G4 = 67
    bA4 = 68
    A4 = 69
    bB4 = 70
    B4 = 71
    C5 = 72
    bD5 = 73
    D5 = 74
    bE5 = 75
    E5 = 76
    F5 = 77
    bG5 = 78
    G5 = 79
    bA5 = 80
    A5 = 81
    bB5 = 82
    B5 = 83
    C6 = 84
    bD6 = 85
    D6 = 86
    bE6 = 87
    E6 = 88
    F6 = 89
    bG6 = 90
    G6 = 91
    bA6 = 92
    A6 = 93
    bB6 = 94
    B6 = 95

class Velocities(Enum):
    pppp = 8
    ppp = 20
    pp = 31
    p = 42
    mp = 53
    mf = 64
    f = 80
    ff = 96
    fff = 112
    ffff = 127

class Instrument(Enum):
    AcousticGrandPiano = 0
    BrightAcousticPiano = 1
    ElectricGrandPiano = 2
    HonkyTonkPiano = 3
    RhodesPiano = 4
    ChorusedPiano = 5
    Harpsichord = 6
    Clavinet = 7
    Celesta = 8
    Glockenspiel = 9
    MusicBox = 10
    Vibraphone = 11
    Marimba = 12
    Xylophone = 13
    TubularBells = 14
    Dulcimer = 15
    DrawbarOrgan = 16
    PercussiveOrgan = 17
    RockOrgan = 18
    ChurchOrgan = 19
    ReedOrgan = 20
    Accordion = 21
    Harmonica = 22
    TangoAccordion = 23
    AcousticGuitarNylon = 24
    AcousticGuitarSteel = 25
    ElectricGuitarJazz = 26
    ElectricGuitarClean = 27
    ElectricGuitarMuted = 28
    OverdrivenGuitar = 29
    DistortionGuitar = 30
    GuitarHarmonics = 31
    AcousticBass = 32
    ElectricBassFinger = 33
    ElectricBassPick = 34
    FretlessBass = 35
    SlapBass1 = 36
    SlapBass2 = 37
    SynthBass1 = 38
    SynthBass2 = 39
    Violin = 40
    Viola = 41
    Cello = 42
    Contrabass = 43
    TremoloStrings = 44
    PizzicatoStrings = 45
    OrchestralHarp = 46
    Timpani = 47
    StringEnsemble1 = 48
    StringEnsemble2 = 49
    SynthStrings1 = 50
    SynthStrings2 = 51
    ChoirAahs = 52
    VoiceOohs = 53
    SynthVoice = 54
    OrchestraHit = 55
    Trumpet = 56
    Trombone = 57
    Tuba = 58
    MutedTrumpet = 59
    FrenchHorn = 60
    BrassSection = 61
    SynthBrass1 = 62
    SynthBrass2 = 63
    SopranoSax = 64
    AltoSax = 65
    TenorSax = 66
    BaritoneSax = 67
    Oboe = 68
    EnglishHorn = 69
    Bassoon = 70
    Clarinet = 71
    Piccolo = 72
    Flute = 73
    Recorder = 74
    PanFlute = 75
    BlownBottle = 76
    Shakuhachi = 77
    Whistle = 78
    Ocarina = 79
    Lead1Square = 80
    Lead2Sawtooth = 81
    Lead3Calliope = 82
    Lead4Chiff = 83
    Lead5Charang = 84
    Lead6Voice = 85
    Lead7Fifths = 86
    Lead8BassLead = 87
    Pad1NewAge = 88
    Pad2Warm = 89
    Pad3Polysynth = 90
    Pad5Bowed = 92
    Pad6Metallic = 93
    Pad7Halo = 94
    Pad8Sweep = 95
    FX1Rain = 96
    FX2Soundtrack = 97
    FX3Crystal = 98
    FX4Atmosphere = 99
    FX5Brightness = 100
    FX6Goblins = 101
    FX7Echoes = 102
    FX8SciFi = 103
    Sitar = 104
    Banjo = 105
    Shamisen = 106
    Koto = 107
    Kalimba = 108
    Bagpipe = 109
    Fiddle = 110
    Shanai = 111
    TinkleBell = 112
    Agogo = 113
    SteelDrums = 114
    Woodblock = 115
    TaikoDrum = 116
    MelodicTom = 117
    SynthDrum = 118
    ReverseCymbal = 119
    GuitarFretNoise = 120
    BreathNoise = 121
    Seashore = 122
    BirdTweet = 123
    TelephoneRing = 124
    Helicopter = 125
    Applause = 126
    Gunshot = 127


class Player:
    def __init__(self, tracks, tempo):
        val = c_longlong()
        QueryPerformanceFrequency(pointer(val))
        self.frequency = 1000 / val.value
        self.hmo = c_longlong()
        midiOutOpen(pointer(self.hmo), 0, 0, 0, 0)
        for i, instrument in enumerate(tracks):
            self.ChangeInstrument(i, instrument.value)
        self.tick = 60000 / tempo
        self.stamp = 0
        self.total_tick = 0

    def timestamp(self) -> float:
        val = c_longlong()
        QueryPerformanceCounter(pointer(val))
        return val.value

    def reset_timer_wait(self):
        self.total_tick = 0
        self.stamp = self.timestamp()
        
    def timer_wait(self, d):
        self.total_tick += d
        t = d - 20
        if t > 0:
            Sleep(int(t))
        while (self.timestamp() - self.stamp) * self.frequency < self.total_tick:
            pass

    def close(self):
        midiOutClose(self.hmo)
        
    def SendMidiEvent(self, bStatus, bData1, bData2):
        return midiOutShortMsg(self.hmo, bStatus | (bData1 << 8) | (bData2 << 16))
    
    def NoteOn(self, track, note, velocity):
        return self.SendMidiEvent(0x90 | track, note, velocity)

    def NoteOff(self, track, note):
        return self.SendMidiEvent(0x80 | track, note, 0)

    def ChangeInstrument(self, track, instrument):
        return self.SendMidiEvent(0xC0 | track, instrument, 0)
    
    def play(self, records):
        self.reset_timer_wait()
        notes = []
        for record in records:
            note = record[0]
            if note == 0: # proceed
                val = record[2] + (record[3] << 8)
                while val != 0:
                    min_time = val
                    for rec in notes:
                        if rec[2] < min_time:
                            min_time = rec[2]
                    self.timer_wait(self.tick * min_time)
                    val -= min_time
                    new_notes = []
                    for rec in notes:
                        rec[2] -= min_time
                        if rec[2] == 0:
                            self.NoteOff(rec[1], rec[0].value)
                        else:
                            new_notes.append(rec)
                    notes = new_notes
            else:
                notes.append(record)
                self.NoteOn(record[1], note.value, record[3].value)
            

def proceed(d):
    return [0, 0, d, 0]


if __name__ == '__main__':
    tempo = 240
    tracks = [
        Instrument.Trumpet,
    ]
    player = Player(tracks, tempo)
    player.play([
        [Notes.A4, 0, 1, Velocities.ff],
        proceed(1),
        [Notes.B4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.C5, 0, 1, Velocities.f],
        proceed(1),
        [Notes.D5, 0, 1, Velocities.f],
        proceed(1),
        [Notes.E5, 0, 2, Velocities.ff],
        proceed(2),
        [Notes.A5, 0, 2, Velocities.f],
        proceed(2),
        [Notes.E5, 0, 1, Velocities.ff],
        proceed(1),
        [Notes.E5, 0, 2, Velocities.f],
        proceed(2),
        [Notes.D5, 0, 1, Velocities.f],
        proceed(1),
        [Notes.E5, 0, 1, Velocities.fff],
        proceed(4),
        [Notes.E5, 0, 1, Velocities.ff],
        proceed(1),
        [Notes.D5, 0, 1, Velocities.f],
        proceed(1),
        [Notes.B4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.C5, 0, 1, Velocities.f],
        proceed(1),
        [Notes.E5, 0, 1, Velocities.ff],
        proceed(1),
        [Notes.D5, 0, 1, Velocities.f],
        proceed(1),
        [Notes.C5, 0, 1, Velocities.f],
        proceed(1),
        [Notes.B4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.A4, 0, 2, Velocities.ff],
        proceed(2),
        [Notes.A4, 0, 2, Velocities.f],
        proceed(2),
        [Notes.A4, 0, 2, Velocities.ff],
        proceed(4),
    ])
    Sleep(1000)
    player.close()

同样,传统观点认为,在满足用音为1234567的前提下,自然小调的乐曲的特征有(从严格到宽松):

  1. 主旋律的音以6开始、以6结尾;上面的《青春舞曲》就是一个典型的例子
  2. 主旋律的音不一定以6开始,但是以6结尾;典型的例子是以1或3开头
  3. 主旋律的音不一定以6开始或结尾,但是主旋律与6关系密切

传统观点认为自然小调乐曲特征是“该调式具有柔和、暗淡的听觉特征,常表现出忧郁、唯美的音乐情感”,显然也是片面的——上面那个示例直接以67123开头、以6结尾,显然非常符合自然小调的定义;然而或许带有一点忧伤,但绝不是“柔和”、“暗淡”的。

3. 和声小调与旋律小调

和声小调和旋律小调都是自然小调的变体,其“变”之处在于有时会将自然小调的4、5升一个半音,变成#4、#5;具体在什么样的情况下会变,不确定,要看乐曲的走向。

传统观点严格区分和声小调与旋律小调,认为和声小调只升5,而且是在任何情况下都升这个5,而旋律小调在上行音阶升4和5、在下行音阶维持不变。这种判断规则过于死板,空客在此不矛采用,并且不区分和声小调与旋律小调,下统称为“旋律小调”。

旋律小调示例:绿袖子(主歌)
from ctypes import windll, c_longlong, pointer
from enum import Enum

kernel32 = windll.kernel32
Sleep = kernel32.Sleep
QueryPerformanceCounter = kernel32.QueryPerformanceCounter
QueryPerformanceFrequency = kernel32.QueryPerformanceFrequency
winmm = windll.winmm
midiOutOpen = winmm.midiOutOpen
midiOutClose = winmm.midiOutClose
midiOutShortMsg = winmm.midiOutShortMsg

class Notes(Enum):
    C1 = 24
    bD1 = 25
    D1 = 26
    bE1 = 27
    E1 = 28
    F1 = 29
    bG1 = 30
    G1 = 31
    bA1 = 32
    A1 = 33
    bB1 = 34
    B1 = 35
    C2 = 36
    bD2 = 37
    D2 = 38
    bE2 = 39
    E2 = 40
    F2 = 41
    bG2 = 42
    G2 = 43
    bA2 = 44
    A2 = 45
    bB2 = 46
    B2 = 47
    C3 = 48
    bD3 = 49
    D3 = 50
    bE3 = 51
    E3 = 52
    F3 = 53
    bG3 = 54
    G3 = 55
    bA3 = 56
    A3 = 57
    bB3 = 58
    B3 = 59
    C4 = 60
    bD4 = 61
    D4 = 62
    bE4 = 63
    E4 = 64
    F4 = 65
    bG4 = 66
    G4 = 67
    bA4 = 68
    A4 = 69
    bB4 = 70
    B4 = 71
    C5 = 72
    bD5 = 73
    D5 = 74
    bE5 = 75
    E5 = 76
    F5 = 77
    bG5 = 78
    G5 = 79
    bA5 = 80
    A5 = 81
    bB5 = 82
    B5 = 83
    C6 = 84
    bD6 = 85
    D6 = 86
    bE6 = 87
    E6 = 88
    F6 = 89
    bG6 = 90
    G6 = 91
    bA6 = 92
    A6 = 93
    bB6 = 94
    B6 = 95

class Velocities(Enum):
    pppp = 8
    ppp = 20
    pp = 31
    p = 42
    mp = 53
    mf = 64
    f = 80
    ff = 96
    fff = 112
    ffff = 127

class Instrument(Enum):
    AcousticGrandPiano = 0
    BrightAcousticPiano = 1
    ElectricGrandPiano = 2
    HonkyTonkPiano = 3
    RhodesPiano = 4
    ChorusedPiano = 5
    Harpsichord = 6
    Clavinet = 7
    Celesta = 8
    Glockenspiel = 9
    MusicBox = 10
    Vibraphone = 11
    Marimba = 12
    Xylophone = 13
    TubularBells = 14
    Dulcimer = 15
    DrawbarOrgan = 16
    PercussiveOrgan = 17
    RockOrgan = 18
    ChurchOrgan = 19
    ReedOrgan = 20
    Accordion = 21
    Harmonica = 22
    TangoAccordion = 23
    AcousticGuitarNylon = 24
    AcousticGuitarSteel = 25
    ElectricGuitarJazz = 26
    ElectricGuitarClean = 27
    ElectricGuitarMuted = 28
    OverdrivenGuitar = 29
    DistortionGuitar = 30
    GuitarHarmonics = 31
    AcousticBass = 32
    ElectricBassFinger = 33
    ElectricBassPick = 34
    FretlessBass = 35
    SlapBass1 = 36
    SlapBass2 = 37
    SynthBass1 = 38
    SynthBass2 = 39
    Violin = 40
    Viola = 41
    Cello = 42
    Contrabass = 43
    TremoloStrings = 44
    PizzicatoStrings = 45
    OrchestralHarp = 46
    Timpani = 47
    StringEnsemble1 = 48
    StringEnsemble2 = 49
    SynthStrings1 = 50
    SynthStrings2 = 51
    ChoirAahs = 52
    VoiceOohs = 53
    SynthVoice = 54
    OrchestraHit = 55
    Trumpet = 56
    Trombone = 57
    Tuba = 58
    MutedTrumpet = 59
    FrenchHorn = 60
    BrassSection = 61
    SynthBrass1 = 62
    SynthBrass2 = 63
    SopranoSax = 64
    AltoSax = 65
    TenorSax = 66
    BaritoneSax = 67
    Oboe = 68
    EnglishHorn = 69
    Bassoon = 70
    Clarinet = 71
    Piccolo = 72
    Flute = 73
    Recorder = 74
    PanFlute = 75
    BlownBottle = 76
    Shakuhachi = 77
    Whistle = 78
    Ocarina = 79
    Lead1Square = 80
    Lead2Sawtooth = 81
    Lead3Calliope = 82
    Lead4Chiff = 83
    Lead5Charang = 84
    Lead6Voice = 85
    Lead7Fifths = 86
    Lead8BassLead = 87
    Pad1NewAge = 88
    Pad2Warm = 89
    Pad3Polysynth = 90
    Pad5Bowed = 92
    Pad6Metallic = 93
    Pad7Halo = 94
    Pad8Sweep = 95
    FX1Rain = 96
    FX2Soundtrack = 97
    FX3Crystal = 98
    FX4Atmosphere = 99
    FX5Brightness = 100
    FX6Goblins = 101
    FX7Echoes = 102
    FX8SciFi = 103
    Sitar = 104
    Banjo = 105
    Shamisen = 106
    Koto = 107
    Kalimba = 108
    Bagpipe = 109
    Fiddle = 110
    Shanai = 111
    TinkleBell = 112
    Agogo = 113
    SteelDrums = 114
    Woodblock = 115
    TaikoDrum = 116
    MelodicTom = 117
    SynthDrum = 118
    ReverseCymbal = 119
    GuitarFretNoise = 120
    BreathNoise = 121
    Seashore = 122
    BirdTweet = 123
    TelephoneRing = 124
    Helicopter = 125
    Applause = 126
    Gunshot = 127


class Player:
    def __init__(self, tracks, tempo):
        val = c_longlong()
        QueryPerformanceFrequency(pointer(val))
        self.frequency = 1000 / val.value
        self.hmo = c_longlong()
        midiOutOpen(pointer(self.hmo), 0, 0, 0, 0)
        for i, instrument in enumerate(tracks):
            self.ChangeInstrument(i, instrument.value)
        self.tick = 60000 / tempo
        self.stamp = 0
        self.total_tick = 0

    def timestamp(self) -> float:
        val = c_longlong()
        QueryPerformanceCounter(pointer(val))
        return val.value

    def reset_timer_wait(self):
        self.total_tick = 0
        self.stamp = self.timestamp()
        
    def timer_wait(self, d):
        self.total_tick += d
        t = d - 20
        if t > 0:
            Sleep(int(t))
        while (self.timestamp() - self.stamp) * self.frequency < self.total_tick:
            pass

    def close(self):
        midiOutClose(self.hmo)
        
    def SendMidiEvent(self, bStatus, bData1, bData2):
        return midiOutShortMsg(self.hmo, bStatus | (bData1 << 8) | (bData2 << 16))
    
    def NoteOn(self, track, note, velocity):
        return self.SendMidiEvent(0x90 | track, note, velocity)

    def NoteOff(self, track, note):
        return self.SendMidiEvent(0x80 | track, note, 0)

    def ChangeInstrument(self, track, instrument):
        return self.SendMidiEvent(0xC0 | track, instrument, 0)
    
    def play(self, records):
        self.reset_timer_wait()
        notes = []
        for record in records:
            note = record[0]
            if note == 0: # proceed
                val = record[2] + (record[3] << 8)
                while val != 0:
                    min_time = val
                    for rec in notes:
                        if rec[2] < min_time:
                            min_time = rec[2]
                    self.timer_wait(self.tick * min_time)
                    val -= min_time
                    new_notes = []
                    for rec in notes:
                        rec[2] -= min_time
                        if rec[2] == 0:
                            self.NoteOff(rec[1], rec[0].value)
                        else:
                            new_notes.append(rec)
                    notes = new_notes
            else:
                notes.append(record)
                self.NoteOn(record[1], note.value, record[3].value)
            

def proceed(d):
    return [0, 0, d, 0]


if __name__ == '__main__':
    tempo = 240
    tracks = [
        Instrument.BrightAcousticPiano,
    ]
    player = Player(tracks, tempo)
    player.play([
        [Notes.A3, 0, 2, Velocities.f],
        proceed(2),
        [Notes.C4, 0, 4, Velocities.ff],
        proceed(4),
        [Notes.D4, 0, 2, Velocities.f],
        proceed(2),
        [Notes.E4, 0, 3, Velocities.ff],
        proceed(3),
        [Notes.bG4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.E4, 0, 2, Velocities.f],
        proceed(2),
        [Notes.D4, 0, 4, Velocities.ff],
        proceed(4),
        [Notes.B3, 0, 2, Velocities.f],
        proceed(2),
        [Notes.G3, 0, 3, Velocities.ff],
        proceed(3),
        [Notes.A3, 0, 1, Velocities.f],
        proceed(1),
        [Notes.B3, 0, 2, Velocities.f],
        proceed(2),
        [Notes.C4, 0, 4, Velocities.ff],
        proceed(4),
        [Notes.A3, 0, 2, Velocities.f],
        proceed(2),
        [Notes.A3, 0, 3, Velocities.ff],
        proceed(3),
        [Notes.bA3, 0, 1, Velocities.f],
        proceed(1),
        [Notes.A3, 0, 2, Velocities.f],
        proceed(2),
        [Notes.B3, 0, 4, Velocities.ff],
        proceed(4),
        [Notes.bA3, 0, 2, Velocities.f],
        proceed(2),
        [Notes.E3, 0, 4, Velocities.ff],
        proceed(4),
        
        [Notes.A3, 0, 2, Velocities.f],
        proceed(2),
        [Notes.C4, 0, 4, Velocities.ff],
        proceed(4),
        [Notes.D4, 0, 2, Velocities.f],
        proceed(2),
        [Notes.E4, 0, 3, Velocities.ff],
        proceed(3),
        [Notes.bG4, 0, 1, Velocities.f],
        proceed(1),
        [Notes.E4, 0, 2, Velocities.f],
        proceed(2),
        [Notes.D4, 0, 4, Velocities.ff],
        proceed(4),
        [Notes.B3, 0, 2, Velocities.f],
        proceed(2),
        [Notes.G3, 0, 3, Velocities.ff],
        proceed(3),
        [Notes.A3, 0, 1, Velocities.f],
        proceed(1),
        [Notes.B3, 0, 2, Velocities.f],
        proceed(2),
        [Notes.C4, 0, 3, Velocities.ff],
        proceed(3),
        [Notes.B3, 0, 1, Velocities.f],
        proceed(1),
        [Notes.A3, 0, 2, Velocities.f],
        proceed(2),
        [Notes.bA3, 0, 2, Velocities.ff],
        proceed(2),
        [Notes.bG3, 0, 2, Velocities.f],
        proceed(2),
        [Notes.bA3, 0, 2, Velocities.f],
        proceed(2),
        [Notes.A3, 0, 6, Velocities.ff],
        proceed(6),
        [Notes.A3, 0, 4, Velocities.ff],
        proceed(4),
    ])
    Sleep(1000)
    player.close()

旋律小调的判断就相对容易,因为本身就是在自然小调的基础上进行微调,所以只要抓住关键音#4、#5即可判断,一般来说,有相当比例的#4、#5则为旋律小调。不过也要注意例外:如果#4远多于#5且#4有时不跟3、6一起出现,则有可能是两种教会调式:Lydian调式/Dorian调式。

传统观点认为,和声小调“能产生紧张、戏剧性的音乐效果,比自然小调更强调小调色彩”,而旋律小调“主要用于表现悲伤、愁绪等情感”。可以发现,很多话都是说了等于没说,具体问题还是需要具体分析。

下一章打算开个插曲讲一下局部的音程关系,然后一章是节奏型,然后是复杂调式,然后是和弦等等。

1 个赞

三、再谈音程

第一章说,频率比值越简单,音程越“好听”,可是显然“好听”是个主观可变的概念,音乐的奇妙之处就在于局部性与整体性的对立统一——相同的局部,在不同的整体里面可能感情色彩不同;在某些整体框架下,不同的局部又有不同的风味。

音程在音乐里的表现形式有且仅有两种:1. 两个音相邻着先后出现;2. 两个音同时出现。在讲具体的音程之前,先提几个定律:

  1. 响度定律:人更倾向于感知响度大的音。这是废话,就不多说了。
  2. 时长定律:人更倾向于感知持续时间长的音,这也是废话。
  3. 进行定律:人更倾向于感知后出现的音,而不是先出现的音,这同样是废话。
  4. 高音定律:人更倾向于感知一个音程中频率更高的那个音。这是很自然的,之所以大多数曲目都是高音部分放主旋律、低音部分放伴奏,就是考虑到人对高音的感知更强;当然也有反例,就是音不能特别高或特别低——特别高或低的音会表现得响度不高,从而人也失去对其感知;以及有一些特殊的关系,后面会提到。
  5. “不自然”定律:对于受过一些音乐训练的人来说,更容易感知更“不自然”的音,比如离调音。这个有点类似于Attention机制,不自然的音获得的注意力权重更高。

在具体的音程性质方面,首先讲的是八度音程,第一章说过,两个音的频率是2:1。这个音程最大的性质就是它没有性质——因为两个音听上去几乎是重叠的。

为什么听上去几乎是重叠的?

因为频率背后反映的是相位的周期,对于一个周期函数f而言,相位=f(时间),其频率是f的最小正周期——高中数学告诉我们,如果t是周期函数f的一个周期,那么2t也是f的一个周期。所以说,如果将两个差八度的由完美周期函数产生的音直接叠加(在数学上表现为加权的加法),新函数的频率等于那个较低的音,而改变了音色。事实上在音乐里,这个方法叫做“泛音”,在计算机中也是用于构造柔和音色的一种方法。那为什么我们在钢琴上同时弹一个C4和一个C5,我们能听出来是两个音呢?这是因为:1. 常见乐器的音色并不是完美周期函数,2. 组合以后的函数的周期性减弱,3. 钢琴上的八度并不是完美的2:1频率(嗯,是为了迎合人耳的偏差),4. “高音定律”。

用钢琴演示一下八度音程
from ctypes import windll, c_longlong, pointer
from enum import Enum

kernel32 = windll.kernel32
Sleep = kernel32.Sleep
QueryPerformanceCounter = kernel32.QueryPerformanceCounter
QueryPerformanceFrequency = kernel32.QueryPerformanceFrequency
winmm = windll.winmm
midiOutOpen = winmm.midiOutOpen
midiOutClose = winmm.midiOutClose
midiOutShortMsg = winmm.midiOutShortMsg

class Notes(Enum):
    C1 = 24
    bD1 = 25
    D1 = 26
    bE1 = 27
    E1 = 28
    F1 = 29
    bG1 = 30
    G1 = 31
    bA1 = 32
    A1 = 33
    bB1 = 34
    B1 = 35
    C2 = 36
    bD2 = 37
    D2 = 38
    bE2 = 39
    E2 = 40
    F2 = 41
    bG2 = 42
    G2 = 43
    bA2 = 44
    A2 = 45
    bB2 = 46
    B2 = 47
    C3 = 48
    bD3 = 49
    D3 = 50
    bE3 = 51
    E3 = 52
    F3 = 53
    bG3 = 54
    G3 = 55
    bA3 = 56
    A3 = 57
    bB3 = 58
    B3 = 59
    C4 = 60
    bD4 = 61
    D4 = 62
    bE4 = 63
    E4 = 64
    F4 = 65
    bG4 = 66
    G4 = 67
    bA4 = 68
    A4 = 69
    bB4 = 70
    B4 = 71
    C5 = 72
    bD5 = 73
    D5 = 74
    bE5 = 75
    E5 = 76
    F5 = 77
    bG5 = 78
    G5 = 79
    bA5 = 80
    A5 = 81
    bB5 = 82
    B5 = 83
    C6 = 84
    bD6 = 85
    D6 = 86
    bE6 = 87
    E6 = 88
    F6 = 89
    bG6 = 90
    G6 = 91
    bA6 = 92
    A6 = 93
    bB6 = 94
    B6 = 95

class Velocities(Enum):
    pppp = 8
    ppp = 20
    pp = 31
    p = 42
    mp = 53
    mf = 64
    f = 80
    ff = 96
    fff = 112
    ffff = 127

class Instrument(Enum):
    AcousticGrandPiano = 0
    BrightAcousticPiano = 1
    ElectricGrandPiano = 2
    HonkyTonkPiano = 3
    RhodesPiano = 4
    ChorusedPiano = 5
    Harpsichord = 6
    Clavinet = 7
    Celesta = 8
    Glockenspiel = 9
    MusicBox = 10
    Vibraphone = 11
    Marimba = 12
    Xylophone = 13
    TubularBells = 14
    Dulcimer = 15
    DrawbarOrgan = 16
    PercussiveOrgan = 17
    RockOrgan = 18
    ChurchOrgan = 19
    ReedOrgan = 20
    Accordion = 21
    Harmonica = 22
    TangoAccordion = 23
    AcousticGuitarNylon = 24
    AcousticGuitarSteel = 25
    ElectricGuitarJazz = 26
    ElectricGuitarClean = 27
    ElectricGuitarMuted = 28
    OverdrivenGuitar = 29
    DistortionGuitar = 30
    GuitarHarmonics = 31
    AcousticBass = 32
    ElectricBassFinger = 33
    ElectricBassPick = 34
    FretlessBass = 35
    SlapBass1 = 36
    SlapBass2 = 37
    SynthBass1 = 38
    SynthBass2 = 39
    Violin = 40
    Viola = 41
    Cello = 42
    Contrabass = 43
    TremoloStrings = 44
    PizzicatoStrings = 45
    OrchestralHarp = 46
    Timpani = 47
    StringEnsemble1 = 48
    StringEnsemble2 = 49
    SynthStrings1 = 50
    SynthStrings2 = 51
    ChoirAahs = 52
    VoiceOohs = 53
    SynthVoice = 54
    OrchestraHit = 55
    Trumpet = 56
    Trombone = 57
    Tuba = 58
    MutedTrumpet = 59
    FrenchHorn = 60
    BrassSection = 61
    SynthBrass1 = 62
    SynthBrass2 = 63
    SopranoSax = 64
    AltoSax = 65
    TenorSax = 66
    BaritoneSax = 67
    Oboe = 68
    EnglishHorn = 69
    Bassoon = 70
    Clarinet = 71
    Piccolo = 72
    Flute = 73
    Recorder = 74
    PanFlute = 75
    BlownBottle = 76
    Shakuhachi = 77
    Whistle = 78
    Ocarina = 79
    Lead1Square = 80
    Lead2Sawtooth = 81
    Lead3Calliope = 82
    Lead4Chiff = 83
    Lead5Charang = 84
    Lead6Voice = 85
    Lead7Fifths = 86
    Lead8BassLead = 87
    Pad1NewAge = 88
    Pad2Warm = 89
    Pad3Polysynth = 90
    Pad5Bowed = 92
    Pad6Metallic = 93
    Pad7Halo = 94
    Pad8Sweep = 95
    FX1Rain = 96
    FX2Soundtrack = 97
    FX3Crystal = 98
    FX4Atmosphere = 99
    FX5Brightness = 100
    FX6Goblins = 101
    FX7Echoes = 102
    FX8SciFi = 103
    Sitar = 104
    Banjo = 105
    Shamisen = 106
    Koto = 107
    Kalimba = 108
    Bagpipe = 109
    Fiddle = 110
    Shanai = 111
    TinkleBell = 112
    Agogo = 113
    SteelDrums = 114
    Woodblock = 115
    TaikoDrum = 116
    MelodicTom = 117
    SynthDrum = 118
    ReverseCymbal = 119
    GuitarFretNoise = 120
    BreathNoise = 121
    Seashore = 122
    BirdTweet = 123
    TelephoneRing = 124
    Helicopter = 125
    Applause = 126
    Gunshot = 127


class Player:
    def __init__(self, tracks, tempo):
        val = c_longlong()
        QueryPerformanceFrequency(pointer(val))
        self.frequency = 1000 / val.value
        self.hmo = c_longlong()
        midiOutOpen(pointer(self.hmo), 0, 0, 0, 0)
        for i, instrument in enumerate(tracks):
            self.ChangeInstrument(i, instrument.value)
        self.tick = 60000 / tempo
        self.stamp = 0
        self.total_tick = 0

    def timestamp(self) -> float:
        val = c_longlong()
        QueryPerformanceCounter(pointer(val))
        return val.value

    def reset_timer_wait(self):
        self.total_tick = 0
        self.stamp = self.timestamp()
        
    def timer_wait(self, d):
        self.total_tick += d
        t = d - 20
        if t > 0:
            Sleep(int(t))
        while (self.timestamp() - self.stamp) * self.frequency < self.total_tick:
            pass

    def close(self):
        midiOutClose(self.hmo)
        
    def SendMidiEvent(self, bStatus, bData1, bData2):
        return midiOutShortMsg(self.hmo, bStatus | (bData1 << 8) | (bData2 << 16))
    
    def NoteOn(self, track, note, velocity):
        return self.SendMidiEvent(0x90 | track, note, velocity)

    def NoteOff(self, track, note):
        return self.SendMidiEvent(0x80 | track, note, 0)

    def ChangeInstrument(self, track, instrument):
        return self.SendMidiEvent(0xC0 | track, instrument, 0)
    
    def play(self, records):
        self.reset_timer_wait()
        notes = []
        for record in records:
            note = record[0]
            if note == 0: # proceed
                val = record[2] + (record[3] << 8)
                while val != 0:
                    min_time = val
                    for rec in notes:
                        if rec[2] < min_time:
                            min_time = rec[2]
                    self.timer_wait(self.tick * min_time)
                    val -= min_time
                    new_notes = []
                    for rec in notes:
                        rec[2] -= min_time
                        if rec[2] == 0:
                            self.NoteOff(rec[1], rec[0].value)
                        else:
                            new_notes.append(rec)
                    notes = new_notes
            else:
                notes.append(record)
                self.NoteOn(record[1], note.value, record[3].value)
            

def proceed(d):
    return [0, 0, d, 0]


if __name__ == '__main__':
    tempo = 120
    tracks = [
        Instrument.BrightAcousticPiano,
        Instrument.BrightAcousticPiano,
    ]
    player = Player(tracks, tempo)
    player.play([
        [Notes.C4, 0, 2, Velocities.ff],
        proceed(2),
        [Notes.C5, 0, 2, Velocities.ff],
        proceed(4),
        
        [Notes.C5, 0, 2, Velocities.ff],
        proceed(2),
        [Notes.C4, 0, 2, Velocities.ff],
        proceed(4),
        
        [Notes.C4, 0, 2, Velocities.ff],
        [Notes.C5, 1, 2, Velocities.ff],
        proceed(2),
        [Notes.C4, 0, 2, Velocities.ff],
        [Notes.C5, 1, 2, Velocities.ff],
        proceed(4),
    ])
    Sleep(1000)
    player.close()

八度以上的音程很少单独出现,而且性质和其半音间隔模12形成的八度以内的音程类似,在此省略。

八度以下的音程中,我们从小往大介绍,首先介绍的是小二度,即1个半音。它作为常见调式的最小音程,最鲜明的特点就是“紧密、间隔小”,这个性质可以衍生出其他性质——像说废话,像隔靴搔痒,像第二天早八考试前一晚的紧张复习,又像坚强的蜗牛一步一个脚印往上爬。

用钢琴演示一下小二度音程
from ctypes import windll, c_longlong, pointer
from enum import Enum

kernel32 = windll.kernel32
Sleep = kernel32.Sleep
QueryPerformanceCounter = kernel32.QueryPerformanceCounter
QueryPerformanceFrequency = kernel32.QueryPerformanceFrequency
winmm = windll.winmm
midiOutOpen = winmm.midiOutOpen
midiOutClose = winmm.midiOutClose
midiOutShortMsg = winmm.midiOutShortMsg

class Notes(Enum):
    C1 = 24
    bD1 = 25
    D1 = 26
    bE1 = 27
    E1 = 28
    F1 = 29
    bG1 = 30
    G1 = 31
    bA1 = 32
    A1 = 33
    bB1 = 34
    B1 = 35
    C2 = 36
    bD2 = 37
    D2 = 38
    bE2 = 39
    E2 = 40
    F2 = 41
    bG2 = 42
    G2 = 43
    bA2 = 44
    A2 = 45
    bB2 = 46
    B2 = 47
    C3 = 48
    bD3 = 49
    D3 = 50
    bE3 = 51
    E3 = 52
    F3 = 53
    bG3 = 54
    G3 = 55
    bA3 = 56
    A3 = 57
    bB3 = 58
    B3 = 59
    C4 = 60
    bD4 = 61
    D4 = 62
    bE4 = 63
    E4 = 64
    F4 = 65
    bG4 = 66
    G4 = 67
    bA4 = 68
    A4 = 69
    bB4 = 70
    B4 = 71
    C5 = 72
    bD5 = 73
    D5 = 74
    bE5 = 75
    E5 = 76
    F5 = 77
    bG5 = 78
    G5 = 79
    bA5 = 80
    A5 = 81
    bB5 = 82
    B5 = 83
    C6 = 84
    bD6 = 85
    D6 = 86
    bE6 = 87
    E6 = 88
    F6 = 89
    bG6 = 90
    G6 = 91
    bA6 = 92
    A6 = 93
    bB6 = 94
    B6 = 95

class Velocities(Enum):
    pppp = 8
    ppp = 20
    pp = 31
    p = 42
    mp = 53
    mf = 64
    f = 80
    ff = 96
    fff = 112
    ffff = 127

class Instrument(Enum):
    AcousticGrandPiano = 0
    BrightAcousticPiano = 1
    ElectricGrandPiano = 2
    HonkyTonkPiano = 3
    RhodesPiano = 4
    ChorusedPiano = 5
    Harpsichord = 6
    Clavinet = 7
    Celesta = 8
    Glockenspiel = 9
    MusicBox = 10
    Vibraphone = 11
    Marimba = 12
    Xylophone = 13
    TubularBells = 14
    Dulcimer = 15
    DrawbarOrgan = 16
    PercussiveOrgan = 17
    RockOrgan = 18
    ChurchOrgan = 19
    ReedOrgan = 20
    Accordion = 21
    Harmonica = 22
    TangoAccordion = 23
    AcousticGuitarNylon = 24
    AcousticGuitarSteel = 25
    ElectricGuitarJazz = 26
    ElectricGuitarClean = 27
    ElectricGuitarMuted = 28
    OverdrivenGuitar = 29
    DistortionGuitar = 30
    GuitarHarmonics = 31
    AcousticBass = 32
    ElectricBassFinger = 33
    ElectricBassPick = 34
    FretlessBass = 35
    SlapBass1 = 36
    SlapBass2 = 37
    SynthBass1 = 38
    SynthBass2 = 39
    Violin = 40
    Viola = 41
    Cello = 42
    Contrabass = 43
    TremoloStrings = 44
    PizzicatoStrings = 45
    OrchestralHarp = 46
    Timpani = 47
    StringEnsemble1 = 48
    StringEnsemble2 = 49
    SynthStrings1 = 50
    SynthStrings2 = 51
    ChoirAahs = 52
    VoiceOohs = 53
    SynthVoice = 54
    OrchestraHit = 55
    Trumpet = 56
    Trombone = 57
    Tuba = 58
    MutedTrumpet = 59
    FrenchHorn = 60
    BrassSection = 61
    SynthBrass1 = 62
    SynthBrass2 = 63
    SopranoSax = 64
    AltoSax = 65
    TenorSax = 66
    BaritoneSax = 67
    Oboe = 68
    EnglishHorn = 69
    Bassoon = 70
    Clarinet = 71
    Piccolo = 72
    Flute = 73
    Recorder = 74
    PanFlute = 75
    BlownBottle = 76
    Shakuhachi = 77
    Whistle = 78
    Ocarina = 79
    Lead1Square = 80
    Lead2Sawtooth = 81
    Lead3Calliope = 82
    Lead4Chiff = 83
    Lead5Charang = 84
    Lead6Voice = 85
    Lead7Fifths = 86
    Lead8BassLead = 87
    Pad1NewAge = 88
    Pad2Warm = 89
    Pad3Polysynth = 90
    Pad5Bowed = 92
    Pad6Metallic = 93
    Pad7Halo = 94
    Pad8Sweep = 95
    FX1Rain = 96
    FX2Soundtrack = 97
    FX3Crystal = 98
    FX4Atmosphere = 99
    FX5Brightness = 100
    FX6Goblins = 101
    FX7Echoes = 102
    FX8SciFi = 103
    Sitar = 104
    Banjo = 105
    Shamisen = 106
    Koto = 107
    Kalimba = 108
    Bagpipe = 109
    Fiddle = 110
    Shanai = 111
    TinkleBell = 112
    Agogo = 113
    SteelDrums = 114
    Woodblock = 115
    TaikoDrum = 116
    MelodicTom = 117
    SynthDrum = 118
    ReverseCymbal = 119
    GuitarFretNoise = 120
    BreathNoise = 121
    Seashore = 122
    BirdTweet = 123
    TelephoneRing = 124
    Helicopter = 125
    Applause = 126
    Gunshot = 127


class Player:
    def __init__(self, tracks, tempo):
        val = c_longlong()
        QueryPerformanceFrequency(pointer(val))
        self.frequency = 1000 / val.value
        self.hmo = c_longlong()
        midiOutOpen(pointer(self.hmo), 0, 0, 0, 0)
        for i, instrument in enumerate(tracks):
            self.ChangeInstrument(i, instrument.value)
        self.tick = 60000 / tempo
        self.stamp = 0
        self.total_tick = 0

    def timestamp(self) -> float:
        val = c_longlong()
        QueryPerformanceCounter(pointer(val))
        return val.value

    def reset_timer_wait(self):
        self.total_tick = 0
        self.stamp = self.timestamp()
        
    def timer_wait(self, d):
        self.total_tick += d
        t = d - 20
        if t > 0:
            Sleep(int(t))
        while (self.timestamp() - self.stamp) * self.frequency < self.total_tick:
            pass

    def close(self):
        midiOutClose(self.hmo)
        
    def SendMidiEvent(self, bStatus, bData1, bData2):
        return midiOutShortMsg(self.hmo, bStatus | (bData1 << 8) | (bData2 << 16))
    
    def NoteOn(self, track, note, velocity):
        return self.SendMidiEvent(0x90 | track, note, velocity)

    def NoteOff(self, track, note):
        return self.SendMidiEvent(0x80 | track, note, 0)

    def ChangeInstrument(self, track, instrument):
        return self.SendMidiEvent(0xC0 | track, instrument, 0)
    
    def play(self, records):
        self.reset_timer_wait()
        notes = []
        for record in records:
            note = record[0]
            if note == 0: # proceed
                val = record[2] + (record[3] << 8)
                while val != 0:
                    min_time = val
                    for rec in notes:
                        if rec[2] < min_time:
                            min_time = rec[2]
                    self.timer_wait(self.tick * min_time)
                    val -= min_time
                    new_notes = []
                    for rec in notes:
                        rec[2] -= min_time
                        if rec[2] == 0:
                            self.NoteOff(rec[1], rec[0].value)
                        else:
                            new_notes.append(rec)
                    notes = new_notes
            else:
                notes.append(record)
                self.NoteOn(record[1], note.value, record[3].value)
            

def proceed(d):
    return [0, 0, d, 0]


if __name__ == '__main__':
    tempo = 120
    tracks = [
        Instrument.BrightAcousticPiano,
        Instrument.BrightAcousticPiano,
    ]
    player = Player(tracks, tempo)
    player.play([
        [Notes.B4, 0, 2, Velocities.ff],
        proceed(2),
        [Notes.C5, 0, 2, Velocities.ff],
        proceed(4),
        
        [Notes.C5, 0, 2, Velocities.ff],
        proceed(2),
        [Notes.B4, 0, 2, Velocities.ff],
        proceed(4),
        
        [Notes.B4, 0, 2, Velocities.ff],
        [Notes.C5, 1, 2, Velocities.ff],
        proceed(2),
        [Notes.B4, 0, 2, Velocities.ff],
        [Notes.C5, 1, 2, Velocities.ff],
        proceed(4),
    ])
    Sleep(1000)
    player.close()

听上面那个例子,我们会发现一个好玩的现象: 7 \dot1 (1)、\dot1 7 (2)、\begin{matrix}\dot1\\7\end{matrix} (3)三者的听感完全不同:(1)因为是向上的音阶,根据高音定律,重音在1,所以听上去更像说完废话、或是积极的蜗牛爬;(2)因为是向下的音阶,虽然1更高,但是7时值更长(我的例子后面空了一拍,空拍的时值计入空拍前的音符),实战中会综合考虑两个因素并结合拍的位置判断重音,不过由于这里后出现的是7,所以姑且认为整体情感由7决定,像隔靴搔痒,或者说在平淡中带来一丝丝忧伤或紧张;(3)两个音同时弹,就要考虑其叠加的数学性质了,第一章说过,两个半音的频率差距是12次根号2,首先它是一个无理数,所以两个周期函数叠加不再是周期函数,这就会导致“不和谐”,接下来要考虑它是哪种不和谐,根据“最邻近”这个性质,这个不和谐是小范围内的不安,对应的就是第二天早八考试前一晚的紧张复习(当然可能有读者的复习不是这么收敛的,可能是嚎放的抓狂,那不在空客的讨论范围内了)

感觉这一章的体量比较大啊,因为每个音程都要分析,所以再开几层楼。

接下来分析大二度。

大二度也有个特点是“间隔小”,因为它是间隔第二小的,而且是自然大小调音阶中相邻音的最常见间隔,比小二度要常见,从而比小二度更自然(当然,这也体现在6次根号2比12次根号2更简单)。根据这个性质推导,大二度的特点有——像懵懂少年的好奇,像好奇心得到解答的安心,又像功课尚未写完但又想出去玩的烦躁。

用钢琴演示一下大二度音程
from ctypes import windll, c_longlong, pointer
from enum import Enum

kernel32 = windll.kernel32
Sleep = kernel32.Sleep
QueryPerformanceCounter = kernel32.QueryPerformanceCounter
QueryPerformanceFrequency = kernel32.QueryPerformanceFrequency
winmm = windll.winmm
midiOutOpen = winmm.midiOutOpen
midiOutClose = winmm.midiOutClose
midiOutShortMsg = winmm.midiOutShortMsg

class Notes(Enum):
    C1 = 24
    bD1 = 25
    D1 = 26
    bE1 = 27
    E1 = 28
    F1 = 29
    bG1 = 30
    G1 = 31
    bA1 = 32
    A1 = 33
    bB1 = 34
    B1 = 35
    C2 = 36
    bD2 = 37
    D2 = 38
    bE2 = 39
    E2 = 40
    F2 = 41
    bG2 = 42
    G2 = 43
    bA2 = 44
    A2 = 45
    bB2 = 46
    B2 = 47
    C3 = 48
    bD3 = 49
    D3 = 50
    bE3 = 51
    E3 = 52
    F3 = 53
    bG3 = 54
    G3 = 55
    bA3 = 56
    A3 = 57
    bB3 = 58
    B3 = 59
    C4 = 60
    bD4 = 61
    D4 = 62
    bE4 = 63
    E4 = 64
    F4 = 65
    bG4 = 66
    G4 = 67
    bA4 = 68
    A4 = 69
    bB4 = 70
    B4 = 71
    C5 = 72
    bD5 = 73
    D5 = 74
    bE5 = 75
    E5 = 76
    F5 = 77
    bG5 = 78
    G5 = 79
    bA5 = 80
    A5 = 81
    bB5 = 82
    B5 = 83
    C6 = 84
    bD6 = 85
    D6 = 86
    bE6 = 87
    E6 = 88
    F6 = 89
    bG6 = 90
    G6 = 91
    bA6 = 92
    A6 = 93
    bB6 = 94
    B6 = 95

class Velocities(Enum):
    pppp = 8
    ppp = 20
    pp = 31
    p = 42
    mp = 53
    mf = 64
    f = 80
    ff = 96
    fff = 112
    ffff = 127

class Instrument(Enum):
    AcousticGrandPiano = 0
    BrightAcousticPiano = 1
    ElectricGrandPiano = 2
    HonkyTonkPiano = 3
    RhodesPiano = 4
    ChorusedPiano = 5
    Harpsichord = 6
    Clavinet = 7
    Celesta = 8
    Glockenspiel = 9
    MusicBox = 10
    Vibraphone = 11
    Marimba = 12
    Xylophone = 13
    TubularBells = 14
    Dulcimer = 15
    DrawbarOrgan = 16
    PercussiveOrgan = 17
    RockOrgan = 18
    ChurchOrgan = 19
    ReedOrgan = 20
    Accordion = 21
    Harmonica = 22
    TangoAccordion = 23
    AcousticGuitarNylon = 24
    AcousticGuitarSteel = 25
    ElectricGuitarJazz = 26
    ElectricGuitarClean = 27
    ElectricGuitarMuted = 28
    OverdrivenGuitar = 29
    DistortionGuitar = 30
    GuitarHarmonics = 31
    AcousticBass = 32
    ElectricBassFinger = 33
    ElectricBassPick = 34
    FretlessBass = 35
    SlapBass1 = 36
    SlapBass2 = 37
    SynthBass1 = 38
    SynthBass2 = 39
    Violin = 40
    Viola = 41
    Cello = 42
    Contrabass = 43
    TremoloStrings = 44
    PizzicatoStrings = 45
    OrchestralHarp = 46
    Timpani = 47
    StringEnsemble1 = 48
    StringEnsemble2 = 49
    SynthStrings1 = 50
    SynthStrings2 = 51
    ChoirAahs = 52
    VoiceOohs = 53
    SynthVoice = 54
    OrchestraHit = 55
    Trumpet = 56
    Trombone = 57
    Tuba = 58
    MutedTrumpet = 59
    FrenchHorn = 60
    BrassSection = 61
    SynthBrass1 = 62
    SynthBrass2 = 63
    SopranoSax = 64
    AltoSax = 65
    TenorSax = 66
    BaritoneSax = 67
    Oboe = 68
    EnglishHorn = 69
    Bassoon = 70
    Clarinet = 71
    Piccolo = 72
    Flute = 73
    Recorder = 74
    PanFlute = 75
    BlownBottle = 76
    Shakuhachi = 77
    Whistle = 78
    Ocarina = 79
    Lead1Square = 80
    Lead2Sawtooth = 81
    Lead3Calliope = 82
    Lead4Chiff = 83
    Lead5Charang = 84
    Lead6Voice = 85
    Lead7Fifths = 86
    Lead8BassLead = 87
    Pad1NewAge = 88
    Pad2Warm = 89
    Pad3Polysynth = 90
    Pad5Bowed = 92
    Pad6Metallic = 93
    Pad7Halo = 94
    Pad8Sweep = 95
    FX1Rain = 96
    FX2Soundtrack = 97
    FX3Crystal = 98
    FX4Atmosphere = 99
    FX5Brightness = 100
    FX6Goblins = 101
    FX7Echoes = 102
    FX8SciFi = 103
    Sitar = 104
    Banjo = 105
    Shamisen = 106
    Koto = 107
    Kalimba = 108
    Bagpipe = 109
    Fiddle = 110
    Shanai = 111
    TinkleBell = 112
    Agogo = 113
    SteelDrums = 114
    Woodblock = 115
    TaikoDrum = 116
    MelodicTom = 117
    SynthDrum = 118
    ReverseCymbal = 119
    GuitarFretNoise = 120
    BreathNoise = 121
    Seashore = 122
    BirdTweet = 123
    TelephoneRing = 124
    Helicopter = 125
    Applause = 126
    Gunshot = 127


class Player:
    def __init__(self, tracks, tempo):
        val = c_longlong()
        QueryPerformanceFrequency(pointer(val))
        self.frequency = 1000 / val.value
        self.hmo = c_longlong()
        midiOutOpen(pointer(self.hmo), 0, 0, 0, 0)
        for i, instrument in enumerate(tracks):
            self.ChangeInstrument(i, instrument.value)
        self.tick = 60000 / tempo
        self.stamp = 0
        self.total_tick = 0

    def timestamp(self) -> float:
        val = c_longlong()
        QueryPerformanceCounter(pointer(val))
        return val.value

    def reset_timer_wait(self):
        self.total_tick = 0
        self.stamp = self.timestamp()
        
    def timer_wait(self, d):
        self.total_tick += d
        t = d - 20
        if t > 0:
            Sleep(int(t))
        while (self.timestamp() - self.stamp) * self.frequency < self.total_tick:
            pass

    def close(self):
        midiOutClose(self.hmo)
        
    def SendMidiEvent(self, bStatus, bData1, bData2):
        return midiOutShortMsg(self.hmo, bStatus | (bData1 << 8) | (bData2 << 16))
    
    def NoteOn(self, track, note, velocity):
        return self.SendMidiEvent(0x90 | track, note, velocity)

    def NoteOff(self, track, note):
        return self.SendMidiEvent(0x80 | track, note, 0)

    def ChangeInstrument(self, track, instrument):
        return self.SendMidiEvent(0xC0 | track, instrument, 0)
    
    def play(self, records):
        self.reset_timer_wait()
        notes = []
        for record in records:
            note = record[0]
            if note == 0: # proceed
                val = record[2] + (record[3] << 8)
                while val != 0:
                    min_time = val
                    for rec in notes:
                        if rec[2] < min_time:
                            min_time = rec[2]
                    self.timer_wait(self.tick * min_time)
                    val -= min_time
                    new_notes = []
                    for rec in notes:
                        rec[2] -= min_time
                        if rec[2] == 0:
                            self.NoteOff(rec[1], rec[0].value)
                        else:
                            new_notes.append(rec)
                    notes = new_notes
            else:
                notes.append(record)
                self.NoteOn(record[1], note.value, record[3].value)
            

def proceed(d):
    return [0, 0, d, 0]


if __name__ == '__main__':
    tempo = 120
    tracks = [
        Instrument.BrightAcousticPiano,
        Instrument.BrightAcousticPiano,
    ]
    player = Player(tracks, tempo)
    player.play([
        [Notes.bB4, 0, 2, Velocities.ff],
        proceed(2),
        [Notes.C5, 0, 2, Velocities.ff],
        proceed(4),
        
        [Notes.C5, 0, 2, Velocities.ff],
        proceed(2),
        [Notes.bB4, 0, 2, Velocities.ff],
        proceed(4),
        
        [Notes.bB4, 0, 2, Velocities.ff],
        [Notes.C5, 1, 2, Velocities.ff],
        proceed(2),
        [Notes.bB4, 0, 2, Velocities.ff],
        [Notes.C5, 1, 2, Velocities.ff],
        proceed(4),
    ])
    Sleep(1000)
    player.close()

为了消除C大调刻板印象,这里例子举的是bB4和C5,可以认为是bB大调的12 (1)、21 (2)和 \begin{matrix}2\\1\end{matrix} (3),三者分别对应少年的好奇、好奇心得到解答、矛盾的烦躁,分析过程与小二度类似,留给读者当作习题。嗯。

接下来分析小三度。

与两个二度的“窄”不同,小三度的两个音的间隔是“正常偏小”,总体处于“正常”,就像是正常走路;而且小三度两个音频率之比约为6:5,已经是很简单的分数了,所以小三度的两个音听起来会比二度更和谐。“和谐”加上“正常偏小”,得到的性质就是舒缓、悠扬、闲适(躺平),或者小步快走,或者轻微警告,等等。

用钢琴演示一下小三度音程
from ctypes import windll, c_longlong, pointer
from enum import Enum

kernel32 = windll.kernel32
Sleep = kernel32.Sleep
QueryPerformanceCounter = kernel32.QueryPerformanceCounter
QueryPerformanceFrequency = kernel32.QueryPerformanceFrequency
winmm = windll.winmm
midiOutOpen = winmm.midiOutOpen
midiOutClose = winmm.midiOutClose
midiOutShortMsg = winmm.midiOutShortMsg

class Notes(Enum):
    C1 = 24
    bD1 = 25
    D1 = 26
    bE1 = 27
    E1 = 28
    F1 = 29
    bG1 = 30
    G1 = 31
    bA1 = 32
    A1 = 33
    bB1 = 34
    B1 = 35
    C2 = 36
    bD2 = 37
    D2 = 38
    bE2 = 39
    E2 = 40
    F2 = 41
    bG2 = 42
    G2 = 43
    bA2 = 44
    A2 = 45
    bB2 = 46
    B2 = 47
    C3 = 48
    bD3 = 49
    D3 = 50
    bE3 = 51
    E3 = 52
    F3 = 53
    bG3 = 54
    G3 = 55
    bA3 = 56
    A3 = 57
    bB3 = 58
    B3 = 59
    C4 = 60
    bD4 = 61
    D4 = 62
    bE4 = 63
    E4 = 64
    F4 = 65
    bG4 = 66
    G4 = 67
    bA4 = 68
    A4 = 69
    bB4 = 70
    B4 = 71
    C5 = 72
    bD5 = 73
    D5 = 74
    bE5 = 75
    E5 = 76
    F5 = 77
    bG5 = 78
    G5 = 79
    bA5 = 80
    A5 = 81
    bB5 = 82
    B5 = 83
    C6 = 84
    bD6 = 85
    D6 = 86
    bE6 = 87
    E6 = 88
    F6 = 89
    bG6 = 90
    G6 = 91
    bA6 = 92
    A6 = 93
    bB6 = 94
    B6 = 95

class Velocities(Enum):
    pppp = 8
    ppp = 20
    pp = 31
    p = 42
    mp = 53
    mf = 64
    f = 80
    ff = 96
    fff = 112
    ffff = 127

class Instrument(Enum):
    AcousticGrandPiano = 0
    BrightAcousticPiano = 1
    ElectricGrandPiano = 2
    HonkyTonkPiano = 3
    RhodesPiano = 4
    ChorusedPiano = 5
    Harpsichord = 6
    Clavinet = 7
    Celesta = 8
    Glockenspiel = 9
    MusicBox = 10
    Vibraphone = 11
    Marimba = 12
    Xylophone = 13
    TubularBells = 14
    Dulcimer = 15
    DrawbarOrgan = 16
    PercussiveOrgan = 17
    RockOrgan = 18
    ChurchOrgan = 19
    ReedOrgan = 20
    Accordion = 21
    Harmonica = 22
    TangoAccordion = 23
    AcousticGuitarNylon = 24
    AcousticGuitarSteel = 25
    ElectricGuitarJazz = 26
    ElectricGuitarClean = 27
    ElectricGuitarMuted = 28
    OverdrivenGuitar = 29
    DistortionGuitar = 30
    GuitarHarmonics = 31
    AcousticBass = 32
    ElectricBassFinger = 33
    ElectricBassPick = 34
    FretlessBass = 35
    SlapBass1 = 36
    SlapBass2 = 37
    SynthBass1 = 38
    SynthBass2 = 39
    Violin = 40
    Viola = 41
    Cello = 42
    Contrabass = 43
    TremoloStrings = 44
    PizzicatoStrings = 45
    OrchestralHarp = 46
    Timpani = 47
    StringEnsemble1 = 48
    StringEnsemble2 = 49
    SynthStrings1 = 50
    SynthStrings2 = 51
    ChoirAahs = 52
    VoiceOohs = 53
    SynthVoice = 54
    OrchestraHit = 55
    Trumpet = 56
    Trombone = 57
    Tuba = 58
    MutedTrumpet = 59
    FrenchHorn = 60
    BrassSection = 61
    SynthBrass1 = 62
    SynthBrass2 = 63
    SopranoSax = 64
    AltoSax = 65
    TenorSax = 66
    BaritoneSax = 67
    Oboe = 68
    EnglishHorn = 69
    Bassoon = 70
    Clarinet = 71
    Piccolo = 72
    Flute = 73
    Recorder = 74
    PanFlute = 75
    BlownBottle = 76
    Shakuhachi = 77
    Whistle = 78
    Ocarina = 79
    Lead1Square = 80
    Lead2Sawtooth = 81
    Lead3Calliope = 82
    Lead4Chiff = 83
    Lead5Charang = 84
    Lead6Voice = 85
    Lead7Fifths = 86
    Lead8BassLead = 87
    Pad1NewAge = 88
    Pad2Warm = 89
    Pad3Polysynth = 90
    Pad5Bowed = 92
    Pad6Metallic = 93
    Pad7Halo = 94
    Pad8Sweep = 95
    FX1Rain = 96
    FX2Soundtrack = 97
    FX3Crystal = 98
    FX4Atmosphere = 99
    FX5Brightness = 100
    FX6Goblins = 101
    FX7Echoes = 102
    FX8SciFi = 103
    Sitar = 104
    Banjo = 105
    Shamisen = 106
    Koto = 107
    Kalimba = 108
    Bagpipe = 109
    Fiddle = 110
    Shanai = 111
    TinkleBell = 112
    Agogo = 113
    SteelDrums = 114
    Woodblock = 115
    TaikoDrum = 116
    MelodicTom = 117
    SynthDrum = 118
    ReverseCymbal = 119
    GuitarFretNoise = 120
    BreathNoise = 121
    Seashore = 122
    BirdTweet = 123
    TelephoneRing = 124
    Helicopter = 125
    Applause = 126
    Gunshot = 127


class Player:
    def __init__(self, tracks, tempo):
        val = c_longlong()
        QueryPerformanceFrequency(pointer(val))
        self.frequency = 1000 / val.value
        self.hmo = c_longlong()
        midiOutOpen(pointer(self.hmo), 0, 0, 0, 0)
        for i, instrument in enumerate(tracks):
            self.ChangeInstrument(i, instrument.value)
        self.tick = 60000 / tempo
        self.stamp = 0
        self.total_tick = 0

    def timestamp(self) -> float:
        val = c_longlong()
        QueryPerformanceCounter(pointer(val))
        return val.value

    def reset_timer_wait(self):
        self.total_tick = 0
        self.stamp = self.timestamp()
        
    def timer_wait(self, d):
        self.total_tick += d
        t = d - 20
        if t > 0:
            Sleep(int(t))
        while (self.timestamp() - self.stamp) * self.frequency < self.total_tick:
            pass

    def close(self):
        midiOutClose(self.hmo)
        
    def SendMidiEvent(self, bStatus, bData1, bData2):
        return midiOutShortMsg(self.hmo, bStatus | (bData1 << 8) | (bData2 << 16))
    
    def NoteOn(self, track, note, velocity):
        return self.SendMidiEvent(0x90 | track, note, velocity)

    def NoteOff(self, track, note):
        return self.SendMidiEvent(0x80 | track, note, 0)

    def ChangeInstrument(self, track, instrument):
        return self.SendMidiEvent(0xC0 | track, instrument, 0)
    
    def play(self, records):
        self.reset_timer_wait()
        notes = []
        for record in records:
            note = record[0]
            if note == 0: # proceed
                val = record[2] + (record[3] << 8)
                while val != 0:
                    min_time = val
                    for rec in notes:
                        if rec[2] < min_time:
                            min_time = rec[2]
                    self.timer_wait(self.tick * min_time)
                    val -= min_time
                    new_notes = []
                    for rec in notes:
                        rec[2] -= min_time
                        if rec[2] == 0:
                            self.NoteOff(rec[1], rec[0].value)
                        else:
                            new_notes.append(rec)
                    notes = new_notes
            else:
                notes.append(record)
                self.NoteOn(record[1], note.value, record[3].value)
            

def proceed(d):
    return [0, 0, d, 0]


if __name__ == '__main__':
    tempo = 120
    tracks = [
        Instrument.BrightAcousticPiano,
        Instrument.BrightAcousticPiano,
    ]
    player = Player(tracks, tempo)
    player.play([
        [Notes.A4, 0, 2, Velocities.ff],
        proceed(2),
        [Notes.C5, 0, 2, Velocities.ff],
        proceed(4),
        
        [Notes.C5, 0, 2, Velocities.ff],
        proceed(2),
        [Notes.A4, 0, 2, Velocities.ff],
        proceed(4),
        
        [Notes.A4, 0, 2, Velocities.ff],
        [Notes.C5, 1, 2, Velocities.ff],
        proceed(2),
        [Notes.A4, 0, 2, Velocities.ff],
        [Notes.C5, 1, 2, Velocities.ff],
        proceed(4),
    ])
    Sleep(1000)
    player.close()

这里举的是A4和C5,先低后高和先高后低的听觉区别没有二度那么大了,都有一些乡土气息,这里是高音定律的反例,小三度过于和谐,在多数情况下,除非时长悬殊、或位置特殊、或有其他和声,则主音都是较低的音,至少是不可忽略的。如果两个音同时弹,也非常和谐,听感非常闲适。

接下来分析大三度。

(待编辑)