赞
踩
本项目旨在实现上下文相关的即兴演奏音乐生成。用户可以通过选择主音调式和输入歌曲信息,如BPM(每分钟节拍数)、弦级数和重复次数等参数,来定制所要生成的音乐。
用户首先选择主音调式,这将为生成的音乐设定一个基本的音乐调性。接着,用户输入歌曲信息,包括BPM、弦级数和重复次数等参数,这些参数将决定音乐的节奏和结构。
基于用户选择的主音调式和输入的歌曲信息,项目将通过算法生成上下文相关的即兴演奏音乐。音乐的旋律、和弦进行和节奏等元素将根据用户输入的参数进行自动调整,以符合用户的要求。
通过这个项目,用户可以根据自己的喜好和需求,定制个性化的即兴演奏音乐。无需专业音乐知识,用户只需选择主音调式和输入简单的歌曲信息,便可以生成符合自己心情和风格的音乐作品。这为音乐创作和欣赏带来了更多的自由和乐趣。
本部分包括系统整体结构图和系统流程图。
系统整体结构如图所示。
伴奏制作、即兴旋律生成的流程如图1所示,贝斯模块及GUI (Graphical User Interface,图形用户接口)制作流程如图2所示。
本部分包括Python环境和PC环境配置。
需要Python 3.7及以上配置,安装库包括hmmlearn、numpy、pypianoroll、pygame、mido、musthe、PyQt5、PyQt-tools (仅支持Windows), 在Windows cmd中使用以下命令:
pip install hmmlearn, numpy, pypianoroll, pygame, mido, musthe
安装PyQt 5和PyQt- tools:
pip install sip
pip install PyQt5
pip install PyQt-tools
将当前目录下的4.ui转换输出成4.py文件:
python -m PyQt5.uic.pyuic 4.ui -o 4.py
本程序最终将打包成.exe,在Windows环境下运行。安装能读取并播放MIDI文件程序的用户可自行查看生成文件;反之,在选项卡中选择“自动播放”选项播放生成的MIDI文件。
本项目包括5个模块:钢琴伴奏制作、乐句生成、贝斯伴奏制作、汇总歌曲制作和GUI设计,下面分别给出各模块的功能介绍及相关代码。
用户选择调式、输入和弦级数后,首先,将和弦级数转换为和弦名称;其次,用musthe将和弦名称转换为对应音;最后,根据用户选择的预置节奏型向MIDI中的钢琴轨写入钢琴伴奏。
本部分代码完成添加和弦功能。
#midi_extended/Track.py中的TrackExtended类
def my_chorus_num(self, all_note, length, num=1, velocity=70, channel=0, change=1): #change为1表示级数调用(索引音阶),0表示和弦名称调用(无需索引音阶)
base_note = 60 #基音
base_num = 0
delay = 0
for chord_note in all_note:
for j in range(num):
count = 0
for note in chord_note:
note = base_note + base_num * 12 + sum(self.scale[0:note]) if change else base_note + base_num * 12 + note - 1
super().append(Message('note_off', note=note, velocity=velocity if count else velocity + 10, time=0 if count else round(0.96 * self.meta_time * length), channel=channel))
count = count + 1 #设第一排为重拍
将用户输入的和弦级数(数字1~7)转换为绝对的和弦名称,再用musthe()方法将和弦名称转为当前调式下的音阶。
def change(self, num, key, mode, count=4): #将和弦从数字更改为字符串 d = {'C': 1, 'D': 2, 'E': 3, 'F': 4, 'G': 5, 'A': 6, 'B': 7} result = [] try: if type(num) == int and 0 < num < 8: s = musthe.Scale(musthe.Note(key), mode) scale = [] for i in range(len(s)): scale.append(str(s[i])) for i in range(count): try: #对音阶数7取余 result.append(d[scale[(num - 1 + 2 * i) % 7][0]]) except IndexError: #五声音阶的数目比较少,可能会存在超出索引范围的现象,对5取余 result.append(d[scale[(num - 1 + 2 * i) % 5][0]]) else: raise TypeError('num should be int from 1~7.') except NameError: return self.change(num, key, 'aeolian', count) return result
本项目为4/4拍和3/4拍的歌曲各提供了6种预置节奏型,共12种节奏型的钢琴伴奏,其中预置节奏型由日常演奏经验而得,是常用的节奏型,能适应大多数曲目。
def my_chorus_4_simple(self, chord_progression, type=1, change=1, circulation=1): #4/4拍节奏预置 def mode1(): #模式1 if change: for chord in chord_progression: self.my_chorus(chord, 4) else: self.my_chorus_num(chord_progression, 4) def mode2(): #模式2 if change: for chord in chord_progression: self.my_chorus(chord, 1, 1, 80) self.my_chorus(chord, 1, 1) self.my_chorus(chord, 1, 1, 60) self.my_chorus(chord, 0.5, 2) else: l = len(chord_progression) for j in range(circulation): self.my_chorus_num([chord_progression[j % l]], 1, 1, 80) self.my_chorus_num([chord_progression[j % l]], 1, 1) self.my_chorus_num([chord_progression[j % l]], 1, 1, 60) self.my_chorus_num([chord_progression[j % l]], 0.5, 2) def mode3(): #模式3 if change: for chord in chord_progression: self.my_chorus(chord, 1, 1, 80) self.my_chorus(chord, 1, 1) self.my_chorus(chord, 1.5, 1, 60) self.my_chorus(chord, 0.5) else: l = len(chord_progression) for j in range(circulation): self.my_chorus_num([chord_progression[j % l]], 1, 1, 80) self.my_chorus_num([chord_progression[j % l]], 1, 1) self.my_chorus_num([chord_progression[j % l]], 1.5, 1, 60) self.my_chorus_num([chord_progression[j % l]], 0.5) def mode4(): #模式4 if change: for chord in chord_progression: self.my_chorus(chord, 2, 1, 80) self.my_chorus(chord, 1.5, 1, 60) self.my_chorus(chord, 0.5) else: l = len(chord_progression) for j in range(circulation): self.my_chorus_num([chord_progression[j % l]], 2, 1, 80) self.my_chorus_num([chord_progression[j % l]], 1.5, 1, 60) self.my_chorus_num([chord_progression[j % l]], 0.5) def mode5(): #模式5 if change: for chord in chord_progression: self.my_chorus(chord, 0.5, 1, 80) self.my_chorus(chord, 0.5, 2) self.my_chorus(chord, 1) self.my_chorus(chord, 0.5, 1, 60) self.my_chorus(chord, 0.5, 2) else: l = len(chord_progression) for j in range(circulation): self.my_chorus_num([chord_progression[j % l]], 0.5, 1, 80) self.my_chorus_num([chord_progression[j % l]], 0.5, 2) self.my_chorus_num([chord_progression[j % l]], 1) self.my_chorus_num([chord_progression[j % l]], 0.5, 1, 60) self.my_chorus_num([chord_progression[j % l]], 0.5, 2) def mode6(): #模式6 if change: for chord in chord_progression: self.my_chorus(chord, 0.5, 1, 80) self.my_chorus(chord, 0.5, 2) self.my_chorus(chord, 0.25, 4) self.my_chorus(chord, 0.5, 1, 60) self.my_chorus(chord, 0.5, 1) self.my_chorus(chord, 0.25, 2) else: l = len(chord_progression) for j in range(circulation): self.my_chorus_num([chord_progression[j % l]], 0.5, 1, 80) self.my_chorus_num([chord_progression[j % l]], 0.5, 2) self.my_chorus_num([chord_progression[j % l]], 0.25, 4) self.my_chorus_num([chord_progression[j % l]], 0.5, 1, 60) self.my_chorus_num([chord_progression[j % l]], 0.5, 1) self.my_chorus_num([chord_progression[j % l]], 0.25, 2) type_d = {1: mode1, 2: mode2, 3: mode3, 4: mode4, 5: mode5, 6: mode6} type_d.get(type)() def my_chorus_3_simple(self, chord_progression, type=1, change=1, circulation=1): #3/4拍的预置 def mode1(): #模式1 if change: for chord in chord_progression: self.my_chorus(chord, 3) else: self.my_chorus_num(chord_progression, 3) def mode2(): #模式2 if change: for chord in chord_progression: self.my_chorus(chord, 1, 1, 80) self.my_chorus(chord, 1, 2) else: l = len(chord_progression) for j in range(circulation): self.my_chorus_num([chord_progression[j % l]], 1, 1, 80) self.my_chorus_num([chord_progression[j % l]], 1, 2) def mode3(): #模式3 if change: for chord in chord_progression: self.my_chorus(chord, 1, 1, 80) self.my_chorus(chord, 0.5, 4) else: l = len(chord_progression) for j in range(circulation): self.my_chorus_num([chord_progression[j % l]], 1, 1, 80) self.my_chorus_num([chord_progression[j % l]], 0.5, 4) def mode4(): #模式4 if change: for chord in chord_progression: self.my_chorus(chord, 0.5, 1, 80) self.my_chorus(chord, 1) self.my_chorus(chord, 0.5, 3) else: l = len(chord_progression) for j in range(circulation): self.my_chorus_num([chord_progression[j % l]], 0.5, 1, 80) self.my_chorus_num([chord_progression[j % l]], 1) self.my_chorus_num([chord_progression[j % l]], 0.5, 3) def mode5(): #模式5 if change: for chord in chord_progression: self.my_chorus(chord, 1.5, 1, 80) self.my_chorus(chord, 0.5, 3) else: l = len(chord_progression) for j in range(circulation): self.my_chorus_num([chord_progression[j % l]], 1.5, 1, 80) self.my_chorus_num([chord_progression[j % l]], 0.5, 3) def mode6(): #模式6 if change: for chord in chord_progression: self.my_chorus(chord, 0.75, 1, 80) self.my_chorus(chord, 0.25) self.my_chorus(chord, 0.75) self.my_chorus(chord, 0.25) self.my_chorus(chord, 0.75) self.my_chorus(chord, 0.25) else: l = len(chord_progression) for j in range(circulation): self.my_chorus_num([chord_progression[j % l]], 0.75, 1, 80) self.my_chorus_num([chord_progression[j % l]], 0.25) self.my_chorus_num([chord_progression[j % l]], 0.75) self.my_chorus_num([chord_progression[j % l]], 0.25) self.my_chorus_num([chord_progression[j % l]], 0.75) self.my_chorus_num([chord_progression[j % l]], 0.25) type_d = {1: mode1, 2: mode2, 3: mode3, 4: mode4, 5: mode5, 6: mode6} type_d.get(type)()
使用hmmlearn,利用马尔可夫模型生成旋律和节奏。其中旋律表示为数字1~7,加上0和-1,1~7对应音阶中第1~7音(五声音阶对应五个音),0对应休止符,-1对应延音音符;节奏表示为数字1、2、4、8、16、32、 6、12、24,分别表示全音符、二分音符、四分音符、八分音符等。
音轨内添加一个普通音符/休止符/延音音符的相关代码如下:
#向音轨内添加一个普通音符,此函数由MusicCritique提供 def add_note(self, note, length, modulation=0, base_num=0, delay=0, velocity=90, scale=[0, 2, 2, 1, 2, 2, 2, 1], channel=0, pitch_type=0, tremble_setting=None, bend_setting=None): bpm = self.bpm base_note = 60 + modulation if pitch_type == 0: try: super().append(Message('note_on', note=base_note + base_num * 12 + sum(scale[0:note]), velocity=velocity, time=round(delay * self.meta_time), channel=channel)) super().append(Message('note_off', note=base_note + base_num * 12 + sum(scale[0:note]), velocity=velocity, time=int(round(0.96 * self.meta_time * length)), channel=channel)) except IndexError: #选中五声音阶时,只有五个音,而hmm最多生成七个音 super().append(Message('note_on', note=base_note + base_num * 12 + sum(scale[0:note - 2]), velocity=velocity, time=round(delay * self.meta_time), channel=channel)) super().append(Message('note_off', note=base_note + base_num * 12 + sum(scale[0:note - 2]), velocity=velocity, time=int(round(0.96 * self.meta_time * length)), channel=channel)) elif pitch_type == 1: #颤音 try: pitch = tremble_setting['pitch'] wheel_times = tremble_setting['wheel_times'] super().append(Message('note_on', note=base_note + base_num * 12 + sum(scale[0:note]), velocity=velocity, time=round(delay * self.meta_time), channel=channel)) for i in range(wheel_times): super().append(Message('pitchwheel', pitch=pitch, time=round(0.96 * self.meta_time * length / (2 * wheel_times)), channel=channel)) super().append(Message('pitchwheel', pitch=0, time=0, channel=channel)) super().append(Message('pitchwheel', pitch=-pitch, time=round(0.96 * self.meta_time * length / (2 * wheel_times)), channel=channel)) super().append(Message('pitchwheel', pitch=0, time=0, channel=channel)) super().append(Message('note_off', note=base_note + base_num * 12 + sum(scale[0:note]), velocity=velocity, time=0, channel=channel)) except: print(traceback.format_exc()) elif pitch_type == 2: try: pitch = bend_setting['pitch'] PASDA = bend_setting['PASDA'] #结合PASDA(Prepare-Attack-Sustain-Decay-Aftermath)属性值实现MIDI滑音和颤音效果 prepare_rate = PASDA[0] / sum(PASDA) attack_rate = PASDA[1] / sum(PASDA) sustain_rate = PASDA[2] / sum(PASDA) decay_rate = PASDA[3] / sum(PASDA) aftermath_rate = PASDA[4] / sum(PASDA) super().append(Message('note_on', note=base_note + base_num * 12 + sum(scale[0:note]), velocity=round(100 * velocity), time=round(delay * self.meta_time), channel=channel)) super().append(Message('aftertouch', time=round(0.96 * self.meta_time * length * prepare_rate), channel=channel)) super().append(Message('pitchwheel', pitch=pitch, time=round(0.96 * self.meta_time * length * attack_rate), channel=channel)) super().append(Message('aftertouch', time=round(0.96 * self.meta_time * length * sustain_rate), channel=channel)) super().append(Message('pitchwheel', pitch=0, time=round(0.96 * self.meta_time * length * decay_rate), channel=channel)) super().append(Message('note_off', note=base_note + base_num * 12 + sum(scale[0:note]), velocity=velocity, time=round(0.96 * self.meta_time * length * aftermath_rate), channel=channel)) except: print(traceback.format_exc()) def add_rest(self, length, velocity=80, channel=0): #增加休止符 super().append(Message('note_off', note=0, velocity=velocity, time=round(0.96 * self.meta_time * length), channel=channel)) def add_tenuto(self, length): #增加延音音符 off = super().pop() #list的最后一个音符note_off on = super().pop() #list的最后一个音符note_on off.time = round(off.time + 0.96 * self.meta_time * length) super().append(on) super().append(off)
相关代码如下:
def hmmmelody(): startprob = np.array([0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.10, 0.00, 0.00]) #初始分布 #状态转移矩阵,由日常演奏经验得出 transmat = np.array([[0.05, 0.10, 0.20, 0.15, 0.20, 0.10, 0.05, 0.05, 0.10], [0.10, 0.05, 0.10, 0.20, 0.20, 0.10, 0.10, 0.05, 0.10], [0.20, 0.10, 0.05, 0.10, 0.10, 0.20, 0.10, 0.05, 0.10], [0.10, 0.10, 0.20, 0.05, 0.10, 0.10, 0.20, 0.05, 0.10], [0.10, 0.20, 0.10, 0.10, 0.05, 0.10, 0.20, 0.05, 0.10], [0.05, 0.10, 0.20, 0.25, 0.10, 0.05, 0.10, 0.05, 0.10], [0.05, 0.10, 0.20, 0.10, 0.25, 0.10, 0.05, 0.05, 0.10], [0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.16, 0.00], [0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.16, 0.00]]) means = np.array([[1], [2], [3], [4], [5], [6], [7], [0], [-1]]) #covariance为协方差 covars = .000000000001 * np.tile(np.identity(1), (9, 1, 1)) #identity的参数1要和means每一行中列数对应 #np.identity 制造对角阵,使用np.tile把对角阵复制成4行1列1条的三维矩阵 model = hmm.GaussianHMM(n_components=9, covariance_type="full") model.startprob_ = startprob model.transmat_ = transmat model.means_ = means model.covars_ = covars #产生样本 X, Z = model.sample(50) m = [] for i in range(50): temp = int(round(X[i, 0])) m.append(temp) #print(m) return m
相关代码如下:
def hmmrhythm(): #初始概率 startprob = np.array([0.15, 0.15, 0.20, 0.20, 0.00, 0.00, 0.20, 0.10, 0.00]) transmat = np.array([[0.15, 0.15, 0.10, 0.10, 0.10, 0.10, 0.10, 0.10, 0.10], [0.20, 0.20, 0.20, 0.10, 0.05, 0.05, 0.10, 0.05, 0.05], [0.05, 0.20, 0.20, 0.20, 0.10, 0.05, 0.05, 0.10, 0.05], [0.05, 0.05, 0.20, 0.20, 0.20, 0.10, 0.05, 0.05, 0.10], [0.10, 0.05, 0.05, 0.20, 0.20, 0.20, 0.10, 0.05, 0.05], [0.05, 0.10, 0.05, 0.05, 0.20, 0.20, 0.20, 0.10, 0.05], [0.05, 0.05, 0.10, 0.05, 0.05, 0.20, 0.20, 0.20, 0.10], [0.10, 0.05, 0.05, 0.10, 0.05, 0.05, 0.20, 0.20, 0.20], [0.20, 0.10, 0.05, 0.05, 0.10, 0.05, 0.05, 0.20, 0.20]]) #每个分量的均值 means = np.array([[1], [2], [4], [8], [16], [32], [6], [12], [24]]) #每个分量的协方差 covars = .000000000001 * np.tile(np.identity(1), (9, 1, 1)) #identity的参数1要和means每一行中的列数对应 #np.identity 制造对角阵,使用np.tile把对角阵复制成4行1列1条的三维矩阵 #建立HMM实例并设置参数 model = hmm.GaussianHMM(n_components=9, covariance_type="full") model.startprob_ = startprob model.transmat_ = transmat model.means_ = means model.covars_ = covars #产生样本 X, Z = model.sample(32) for i in range(32): X[i, 0] = int(round(X[i, 0])) r = X[:, 0] sum = 0 i = 0 new_r = [] while 1 - sum > 0: sum += 1 / r[i] new_r.append(1 / r[i]) i += 1 new_r[i - 1] = 0 new_r[i - 1] = 1 - np.sum(new_r) #print(r[0:i - 1]) #print(new_r) #print(np.sum (new_r)) return new_r
为丰富曲目内容,预置14组贝斯供用户选择。
向歌曲内添加贝斯轨的相关代码如下:
#向音轨内添加一个普通音符,此函数由MusicCritique提供
def add_bass(self, note, length, base_num=-2, velocity=1.0, channel=6, delay=0):
bpm = self.bpm
scale = self.scale
base_note = 60
super().append(Message('note_on', note=base_note + base_num * 12 + sum(self.scale[0:note]), velocity=round(80 * velocity), time=round(delay * self.meta_time), channel=channel))
super().append(Message('note_off', note=base_note + base_num * 12 + sum(self.scale[0:note]), velocity=round(80 * velocity), time=int(round(0.96 * self.meta_time * length)), channel=channel))
根据日常演奏经验,对4/4拍和3/4拍的歌曲分别给出7种常用贝斯轨,共14种,其中函数的输入chord_progression可以是和弦名称,也可以是和弦级数。例如:
self.chord_progression = ['Fmaj7', 'Em7', 'Dm7', 'Cmaj7']或self.chord_progression = '4321'。 def my_bass_4_simple(self, chord_progression, type=1, change=1): d = {'C': 1, 'D': 2, 'E': 3, 'F': 4, 'G': 5, 'A': 6, 'B': 7} #这里只用7个音,因为add_bass内部已经有对音阶的索引,所以不用12个音 def mode1(): for chord in chord_progression: self.add_bass(d[chord[0]] if change else int(chord),0.25)#根音 self.add_rest(0.25) #休止 self.add_bass(d[chord[0]] if change else int(chord), 0.25) self.add_rest(0.5) self.add_bass(d[chord[0]] if change else int(chord), 0.25) self.add_rest(0.5) self.add_bass(d[chord[0]] if change else int(chord), 1) self.add_bass((d[chord[0]] + 4) % 7 if change else (int(chord) + 4) % 7, 1) #五音 #此处对和弦名与和弦级数做了兼容处理,函数的输入可以是和弦名也可以是和弦级数 #change为1表示和弦名,为0表示和弦级数,在add_bass里会做相应的处理 def mode2(): for chord in chord_progression: for i in range(16): #十六音符根音 self.add_bass(d[chord[0]] if change else int(chord), 0.25) def mode3(): for chord in chord_progression: self.add_bass(d[chord[0]] if change else int(chord),0.5)#根音 self.add_bass(d[chord[0]] if change else int(chord), 0.5) self.add_rest(0.5) self.add_bass((d[chord[0]] + 4) % 7 if change else (int(chord) + 4) % 7, 0.5) #五音 self.add_bass(d[chord[0]] if change else int(chord), 0.5) self.add_bass(d[chord[0]] if change else int(chord), 0.5) self.add_rest(0.5) self.add_bass((d[chord[0]] - 1) % 7 if change else (int(chord) - 1) % 7, 0.5) #七音 def mode4(): for chord in chord_progression: #行进贝司 self.add_bass(d[chord[0]] if change else int(chord), 1)#根音 self.add_bass((d[chord[0]] + 4) % 7 if change else (int(chord) + 4) % 7, 1) #五音 self.add_bass(d[chord[0]] if change else int(chord), 1)#根音 self.add_bass((d[chord[0]] - 1) % 7 if change else (int(chord) - 1) % 7, 0.75) #七音 self.add_bass((d[chord[0]] - 1) % 7 if change else (int(chord) - 1) % 7, 0.25) #七音 def mode5(): for chord in chord_progression: self.add_bass(d[chord[0]] if change else int(chord), 1)#根音 self.add_bass(d[chord[0]] if change else int(chord), 1)#根音 self.add_bass(d[chord[0]] if change else int(chord),1.5)#根音 self.add_bass((d[chord[0]] - 1) % 7 if change else (int(chord) - 1) % 7, 0.5) #七音 def mode6(): i = 0 for chord in chord_progression: #击弦贝司 if i % 2 == 0: self.add_bass(d[chord[0]] if change else int(chord), 0.25, channel=8) #根音 self.add_rest(0.5) self.add_bass((d[chord[0]] + 4) % 7 if change else (int(chord) + 4) % 7, 0.25, channel=8) #五音 self.add_rest(0.5) self.add_bass(d[chord[0]] if change else int(chord), 0.5, channel=8) #根音 self.add_bass(d[chord[0]] if change else int(chord), 0.25, channel=8) #根音 self.add_rest(0.5) self.add_bass((d[chord[0]] + 4) % 7 if change else (int(chord) + 4) % 7, 0.25, channel=8) #五音 self.add_rest(0.5) self.add_bass(d[chord[0]] if change else int(chord), 0.5, channel=8) #根音 else: self.add_bass(d[chord[0]] if change else int(chord), 0.25, channel=8) #根音 self.add_rest(0.5) self.add_bass(d[chord[0]] if change else (int(chord) + 4) % 7, 0.25, channel=8) #五音 self.add_rest(0.5) self.add_bass(d[chord[0]] if change else int(chord), 0.5, channel=8) #根音 self.add_bass(d[chord[0]] if change else int(chord), 0.5, channel=8) #根音 self.add_bass(d[chord[0]] if change else int(chord), 0.5, channel=8) #根音 self.add_bass((d[chord[0]] + 4) % 7 if change else (int(chord) + 4) % 7, 0.5, channel=8) #五音 self.add_bass(d[chord[0]] if change else int(chord), 0.5, channel=8) #根音 i += 1 def mode7(): i = 0 for chord in chord_progression: i += 1 if i % 2: elf.add_bass(d[chord[0]] if change else int(chord), 2)#根音 self.add_bass((d[chord[0]] + 4) % 7 if change else (int(chord) + 4) % 7, 2) #五音 else: self.add_bass(d[chord[0]] if change else int(chord), 2) #根音 self.add_bass((d[chord[0]] + 4) % 7 if change else (int(chord) + 4) % 7, 1) #五音 self.add_bass(d[chord[0]] if change else int(chord), 1) #根音 type_d = {1: mode1, 2: mode2, 3: mode3, 4: mode4, 5: mode5, 6: mode6, 7: mode7} type_d.get(type)() def my_bass_3_simple(self, chord_progression, type=1, change=1): d = {'C': 1, 'D': 2, 'E': 3, 'F': 4, 'G': 5, 'A': 6, 'B': 7} def mode1(): i = 0 for chord in chord_progression: i += 1 if i % 2: self.add_bass(d[chord[0]] if change else int(chord), 2) #根音 self.add_bass((d[chord[0]] + 4) % 7 if change else (int(chord) + 4) % 7, 1) #五音 else: self.add_bass(d[chord[0]] if change else int(chord), 1) #根音 self.add_bass((d[chord[0]] + 4) % 7 if change else (int(chord) + 4) % 7, 1) #五音 self.add_bass(d[chord[0]] if change else int(chord), 1) #根音 def mode2(): for chord in chord_progression: self.add_bass(d[chord[0]] if change else int(chord), 3) #根音 def mode3(): i = 0 for chord in chord_progression: i += 1 if i % 2: self.add_bass((d[chord[0]] - 1) % 7 if change else (int(chord) - 1) % 7, 1) #七音 self.add_bass(d[chord[0]] if change else int(chord), 1) #根音 self.add_bass((d[chord[0]] + 2) % 7 if change else (int(chord) + 2) % 7, 1) #三音 else: self.add_bass((d[chord[0]] - 1) % 7 if change else (int(chord) - 1) % 7, 1) #七音 self.add_bass(d[chord[0]] if change else int(chord), 1) #根音 self.add_bass((d[chord[0]] - 1) % 7 if change else (int(chord) - 1) % 7, 1) #七音 def mode4(): for chord in chord_progression: self.add_bass(d[chord[0]] if change else int(chord), 1) #根音 self.add_bass(d[chord[0]] if change else int(chord), 1.5) #根音 self.add_bass((d[chord[0]] + 4) % 7 if change else int(chord) + 4, 0.5) #五音 def mode5(): i = 0 for chord in chord_progression: i += 1 if i % 2: self.add_bass(d[chord[0]] if change else int(chord), 0.5) #根音 self.add_bass(d[chord[0]] if change else int(chord), 0.5) #根音 self.add_bass((d[chord[0]] + 4) % 7 if change else (int(chord) + 4) % 7, 1) #五音 self.add_bass(d[chord[0]] if change else int(chord), 1) #根音 else: self.add_bass(d[chord[0]] if change else int(chord), 0.5) #根音 self.add_bass(d[chord[0]] if change else int(chord), 0.5) #根音 self.add_bass((d[chord[0]] + 4) % 7 if change else (int(chord) + 4) % 7, 1) #五音 self.add_bass(d[chord[0]] % 7 if change else int(chord), 0.5) #根音 self.add_bass((d[chord[0]] - 1) % 7 if change else (int(chord) - 1) % 7, 0.5) #七音 def mode6(): for chord in chord_progression: self.add_bass(d[chord[0]] if change else int(chord), 0.5) #根音 self.add_bass(d[chord[0]] if change else int(chord), 0.5) #根音 self.add_bass((d[chord[0]] + 4) % 7 if change else (int(chord) + 4) % 7, 0.5) #五音 self.add_bass(d[chord[0]] if change else int(chord), 0.5) #根音 self.add_bass((d[chord[0]] - 1) % 7 if change else (int(chord) - 1) % 7, 0.5) #五音 self.add_bass(d[chord[0]] if change else int(chord), 0.5) #根音 def mode7(): for chord in chord_progression: self.add_bass(d[chord[0]] if change else int(chord), 0.25) #根音 self.add_rest(0.25) #根音 self.add_bass(d[chord[0]] if change else int(chord), 0.25) #根音 self.add_rest(0.5) #根音 self.add_bass((d[chord[0]] + 4) % 7 if change else int(chord) + 4, 0.25) #五音 self.add_rest(0.5) #根音 self.add_bass(d[chord[0]] if change else int(chord), 0.5) #根音 self.add_bass((d[chord[0]] - 1) % 7 if change else (int(chord) - 1) % 7, 0.5) #根音 type_d = {1: mode1, 2: mode2, 3: mode3, 4: mode4, 5: mode5, 6: mode6, 7: mode7} type_d.get(type)()
完成钢琴伴奏制作、乐句生成模块后,使用类Impromptu
调用并完成MIDI文件的写入与播放,并使用装饰器完成日志记录,函数piano_roll_test()
实现音乐的可视化。
制作歌曲时记录日志,在当前目录下生成日志文件。
import logging
from functools import wraps
def decorator_log(fun):
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', datefmt='%a, %d %b %Y %H:%M:%S', filename='./test.log', filemode='w')
@wraps(fun)
def fun_in(*args, **kwargs):
logging.debug("{} start.".format(fun.__name__))
fun(*args, **kwargs)
logging.debug("{} end.".format(fun.__name__))
return fun_in
相关代码如下:
#将音乐的旋律高低与时值长短可视化,保存图片于my/data下,函数由MusicCritique提供
def piano_roll_test(self):
path = self.file_path
mid = MidiFileExtended(path, 'r')
mid.turn_track_into_numpy_matrix('Piano', "../my/data/Piano.npy")
mid.generate_track_from_numpy_matrix("../my/data/Piano.npy", (288, 128), 'Piano', False, True, '../my/data/Piano.png')
mid.turn_track_into_numpy_matrix('Melody', "../my/data/Melody.npy")
mid.generate_track_from_numpy_matrix("../my/data/Melody.npy", (288, 128), 'Melody', False, True, '../my/data/Melody.png')
if self.sw_bass:
mid.turn_track_into_numpy_matrix('Bass', "../my/data/Bass.npy")
mid.generate_track_from_numpy_matrix("../my/data/Bass.npy",(288, 128), 'Bass', False, True, '../my/data/Bass.png')
后台所有功能汇总类,在此实现即兴曲目的信息输入,可以保存并播放文件,实现音乐可视化。
print("Import start.") #初次运行import时间较长 from midi_extended.MidiFileExtended import MidiFileExtended import my.Q_myhmm import my.decorator_log import numpy as np import time print("Import end.") class Impromptu: """ 根据和弦进程自动生成旋律。 bpm:正整数,每分钟心跳数 time_signature:每个小节中的节拍数,通常为n/m 默认的节奏伴奏目前仅支持4/4或3/4 Key:音符可以是C D E F G A B Mode:定即兴的规模 file_path:MIDI文件的路径和名称 chord_progression:和弦列表 intensity:水平即兴演奏和垂直即兴演奏的参数 repeat:重复相同和弦进行的时间 """ def __init__(self): self.bpm = 120 self.time_signature = '4/4' self.key = 'C' self.mode = 'major' self.file_path = '../my/data/song.mid' #self.chord_progression = ['Fmaj7', 'Em7', 'Dm7', 'Cmaj7'] #self.chord_progression = ['Cmaj7', 'Am7', 'F', 'E7'] self.chord_progression = '4321' #列表是已经整理好数据类型,即列表内全是字符(和弦名)或全是级数(由GUI 检查) self.intensity = 0 self.repeat = 1 self.mid = MidiFileExtended(self.file_path, type=1, mode='w') self.accompany_type = 1 self.sw_bass = False self.bass_type = 1 self.silent = False self.accompany_tone = 4 if self.silent else 0 self.note_tone = 26 if self.silent else 0 @property def scale(self): #定义各种参数 d = {'major': [0, 2, 2, 1, 2, 2, 2, 1], 'dorian': [0, 2, 1, 2, 2, 2, 1, 2], 'phrygian': [0, 1, 2, 2, 2, 1, 2, 2], 'lydian': [0, 2, 2, 2, 1, 2, 2, 1], 'mixolydian': [0, 2, 2, 1, 2, 2, 1, 2], 'minor': [0, 2, 1, 2, 2, 1, 2, 2], 'locrian': [0, 1, 2, 2, 1, 2, 2, 2], 'major_pentatonic': [0, 2, 2, 3, 2, 3], 'minor_pentatonic': [0, 3, 2, 2, 3, 2]} aliases = {'Ionian': 'major', 'aeolian': 'minor'} try: self._scale = d[self.mode.lower()] except KeyError: #异常处理 try: self._scale = d[aliases[self.mode]] except KeyError: raise KeyError('Can not find your mode. Please check your key.') return self._scale @my.decorator_log.decorator_log def chorus(self): track = self.mid.get_extended_track('Piano') track.scale = self.scale if type(self.chord_progression) == list: #绝对的和弦名称 for i in range(self.repeat): if self.time_signature[0] == '4': track.my_chorus_4_simple(chord_progression=self.chord_progression, type=2) else: track.my_chorus_3_simple(chord_progression=self.chord_progression, type=2) else: #是级数表示的和弦 change_result = [] for chord in self.chord_progression: change_result.append(track.change(int(chord),self.key, self.mode)) for i in range(self.repeat): if self.time_signature[0] == '4': track.my_chorus_4_simple(change_result, type=1, change=0, circulation=len(self.chord_progression)) else: track.my_chorus_3_simple(change_result, type=1, change=0, circulation=len(self.chord_progression)) print("To specify the accompaniment, you can also call function in ./midi_extended/Track.py/my_chorus") help(track.my_chorus) @my.decorator_log.decorator_log def note(self): track = self.mid.get_extended_track('Melody') #track.print_msgs() for j in range(self.repeat): print("\r note {} is making...".format(j + 1), end="") for chord in self.chord_progression: melody = my.Q_myhmm.hmmmelody() rhythm = my.Q_myhmm.hmmrhythm() multiple = int(self.time_signature[0]) d = {'C': 1, 'Db': 2, 'D': 3, 'Eb': 4, 'E': 5, 'F': 6, 'Gb': 7, 'G': 8, 'Ab': 9, 'A': 10, 'Bb': 11, 'B': 12, 'C#': 2, 'D#': 4, 'F#': 7, 'G#': 9, 'A#': 11} if self.intensity: np.random.seed(round(1000000 * time.time()) % 100) # Seed must be between 0 and 2**32 - 1 p = np.array([self.intensity, 1 - self.intensity]) if type(self.chord_progression) == list: #已经是绝对的和弦名称 modulation = np.random.choice([0, d[chord[0]]], p=p.ravel()) else: #是级数 modulation = np.random.choice([0, sum(self.scale[0:int(chord)])], p=p.ravel()) else: modulation = 0 #modulation = sum(self.scale[0:d[chord[0]]]) for i in range(len(rhythm)): m = melody[i] if m > 0: #是一个普通音符 track.add_note(m, rhythm[i] * multiple, modulation, 1, velocity=110, scale=self.scale, channel=3) #sum(self.scale[0:d[chord[0]]])) #print('add_note', m, rhythm[i]*multiple) elif m == 0: #是休止符 track.add_rest(rhythm[i] * multiple) #print('add_rest', m, rhythm[i]*multiple) else: #是延音符 track.add_tenuto(rhythm[i] * multiple) #print('add_tenuto', m, rhythm[i]*multiple) def bass(self): track = self.mid.get_extended_track('Bass') track.scale = self.scale if type(self.chord_progression) == list: #已经是绝对的和弦名称 for i in range(self.repeat): if self.time_signature[0] == '4': track.my_bass_4_simple(chord_progression=self.chord_progression, type=1) else: track.my_bass_3_simple(chord_progression=self.chord_progression, type=1) else: #是级数表示的和弦 for i in range(self.repeat): if self.time_signature[0] == '4': track.my_bass_4_simple(self.chord_progression, type=i + 1, change=0) else: track.my_bass_3_simple(self.chord_progression, type=i + 1, change=0) def piano_roll_test(self): #定义测试 path = self.file_path #文件路径 mid = MidiFileExtended(path, 'r') mid.turn_track_into_numpy_matrix('Piano', "../my/data/Piano.npy") mid.generate_track_from_numpy_matrix("../my/data/Piano.npy", (288, 128), 'Piano', False, True, '../my/data/Piano.png') mid.turn_track_into_numpy_matrix('Melody', "../my/data/Melody.npy") #数据写入矩阵 mid.generate_track_from_numpy_matrix("../my/data/Melody.npy", (288, 128), 'Melody', False, True, '../my/data/Melody.png') if self.sw_bass: mid.turn_track_into_numpy_matrix('Bass', "../my/data/Bass.npy") mid.generate_track_from_numpy_matrix("../my/data/Bass.npy", (288, 128), 'Bass', False, True, '../my/data/Bass.png') def write_song(self): #定义写入歌曲 del self.mid self.mid = MidiFileExtended(self.file_path, type=1, mode='w') self.mid.add_new_track('Piano', self.time_signature, self.bpm, self.key, {'0': 4 if self.silent else 0}) #4}) #这里的轨道0和1音色是30,代表具体乐器音色 self.chorus() self.mid.add_new_track('Melody', self.time_signature, self.bpm, self.key, {'3': 26 if self.silent else 0}) #26 self.note() if self.sw_bass: self.mid.add_new_track('Bass', self.time_signature, self.bpm, self.key, {'6': 39 if self.silent else 33, '7': 35, '8': 36}) self.bass() if __name__ == '__main__': #主函数 silence = Impromptu() print(silence.scale) silence.write_song() silence.mid.save_midi() silence.piano_roll_test() print("Done. Start to play.") #silence.mid.play_it()
为方便用户交互,使用PyQt5 Designer拖动控件,设计图形界面,使用PyQt-tools将制作好的.ui文件转为.py代码,完成用户界面初始化,再将控件绑定对应功能。
此代码由制作的.ui文件经PyQt-tools转换而成,在Windows下运行。
from PyQt5 import QtCore, QtWidgets class Ui_MainWindow(object): def setupUi(self, MainWindow): #设置界面 MainWindow.setObjectName("MainWindow") MainWindow.resize(800, 680) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget) self.verticalLayoutWidget.setGeometry(QtCore.QRect(0, 0, 800, 680)) self.verticalLayoutWidget.setObjectName("verticalLayoutWidget") self.verticalLayout_2=QtWidgets.QVBoxLayout(self.verticalLayoutWidget) self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) self.verticalLayout_2.setObjectName("verticalLayout_2") self.verticalLayout_3 = QtWidgets.QVBoxLayout() self.verticalLayout_3.setObjectName("verticalLayout_3") self.frame = QtWidgets.QFrame(self.verticalLayoutWidget) self.frame.setEnabled(True) self.frame.setStyleSheet("background-image:url(./background.jpeg);") self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel) self.frame.setFrameShadow(QtWidgets.QFrame.Raised) self.frame.setObjectName("frame") self.w_time_signalture = QtWidgets.QComboBox(self.frame) self.w_time_signalture.setGeometry(QtCore.QRect(360,220, 111, 21)) self.w_time_signalture.setObjectName("w_time_signalture") self.w_time_signalture.addItem("") self.w_time_signalture.addItem("") self.w_mode = QtWidgets.QComboBox(self.frame) self.w_mode.setGeometry(QtCore.QRect(360, 140, 111, 21)) self.w_mode.setObjectName("w_mode") self.w_mode.addItem("") self.w_mode.addItem("") self.w_mode.addItem("") self.w_mode.addItem("") self.w_mode.addItem("") self.w_mode.addItem("") self.w_mode.addItem("") self.w_mode.addItem("") self.w_mode.addItem("") self.w_bass = QtWidgets.QComboBox(self.frame) self.w_bass.setGeometry(QtCore.QRect(360, 300, 111, 21)) self.w_bass.setObjectName("w_bass") self.w_bass.addItem("") self.w_bass.addItem("") self.w_bass.addItem("") self.w_bass.addItem("") self.w_bass.addItem("") self.w_bass.addItem("") self.w_bass.addItem("") self.w_bass.addItem("") self.w_accompany = QtWidgets.QComboBox(self.frame) self.w_accompany.setGeometry(QtCore.QRect(360, 260, 111, 21)) self.w_accompany.setObjectName("w_accompany") self.w_accompany.addItem("") self.w_accompany.addItem("") self.w_accompany.addItem("") self.w_accompany.addItem("") self.w_accompany.addItem("") self.w_accompany.addItem("") self.w_key = QtWidgets.QComboBox(self.frame) self.w_key.setEnabled(True) self.w_key.setGeometry(QtCore.QRect(360, 100, 111, 21)) self.w_key.setMaxVisibleItems(7) self.w_key.setObjectName("w_key") self.w_key.addItem("") self.w_key.addItem("") self.w_key.addItem("") self.w_key.addItem("") self.w_key.addItem("") self.w_key.addItem("") self.w_key.addItem("") self.verticalLayout_3.addWidget(self.frame) self.verticalLayout_2.addLayout(self.verticalLayout_3) self.label = QtWidgets.QLabel(self.centralwidget) self.label.setGeometry(QtCore.QRect(190, 340, 141, 31)) self.label.setObjectName("label") self.w_play = QtWidgets.QPushButton(self.centralwidget) self.w_play.setGeometry(QtCore.QRect(360, 490, 93, 28)) self.w_play.setAutoDefault(False) self.w_play.setDefault(False) self.w_play.setFlat(False) self.w_play.setObjectName("w_play") self.label_7 = QtWidgets.QLabel(self.centralwidget) self.label_7.setGeometry(QtCore.QRect(190, 380, 121, 31)) self.label_7.setObjectName("label_7") self.w_intensity = QtWidgets.QSlider(self.centralwidget) self.w_intensity.setGeometry(QtCore.QRect(340, 430, 160, 22)) self.w_intensity.setMaximum(100) self.w_intensity.setSingleStep(1) self.w_intensity.setOrientation(QtCore.Qt.Horizontal) self.w_intensity.setObjectName("w_intensity") self.w_repeat = QtWidgets.QLineEdit(self.centralwidget) self.w_repeat.setGeometry(QtCore.QRect(360, 380, 113, 21)) self.w_repeat.setInputMask("") self.w_repeat.setMaxLength(32767) self.w_repeat.setObjectName("w_repeat") self.w_bpm = QtWidgets.QLineEdit(self.centralwidget) self.w_bpm.setGeometry(QtCore.QRect(360, 180, 113, 21)) self.w_bpm.setObjectName("w_bpm") self.w_chord_progression = QtWidgets.QLineEdit(self.centralwidget) self.w_chord_progression.setGeometry(QtCore.QRect(360,340,113,21)) self.w_chord_progression.setObjectName("w_chord_progression") self.label_6 = QtWidgets.QLabel(self.centralwidget) self.label_6.setGeometry(QtCore.QRect(190, 180, 121, 31)) self.label_6.setObjectName("label_6") self.label_5 = QtWidgets.QLabel(self.centralwidget) self.label_5.setGeometry(QtCore.QRect(190, 100, 121, 31)) self.label_5.setObjectName("label_5") self.label_3 = QtWidgets.QLabel(self.centralwidget) self.label_3.setGeometry(QtCore.QRect(190, 420, 121, 31)) self.label_3.setObjectName("label_3") self.label_2 = QtWidgets.QLabel(self.centralwidget) self.label_2.setGeometry(QtCore.QRect(190, 220, 121, 31)) self.label_2.setObjectName("label_2") self.label_4 = QtWidgets.QLabel(self.centralwidget) self.label_4.setGeometry(QtCore.QRect(190, 140, 121, 31)) self.label_4.setObjectName("label_4") self.label_8 = QtWidgets.QLabel(self.centralwidget) self.label_8.setGeometry(QtCore.QRect(190, 260, 121, 31)) self.label_8.setObjectName("label_8") self.label_9 = QtWidgets.QLabel(self.centralwidget) self.label_9.setGeometry(QtCore.QRect(190, 300, 121, 31)) self.label_9.setObjectName("label_9") self.checkBox = QtWidgets.QCheckBox(self.centralwidget) self.checkBox.setGeometry(QtCore.QRect(630, 430, 91, 19)) self.checkBox.setObjectName("checkBox") MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 798, 26)) self.menubar.setObjectName("menubar") self.menusetting = QtWidgets.QMenu(self.menubar) self.menusetting.setObjectName("menusetting") self.menuhelp = QtWidgets.QMenu(self.menubar) self.menuhelp.setObjectName("menuhelp") self.menuAbout = QtWidgets.QMenu(self.menubar) self.menuAbout.setObjectName("menuAbout") MainWindow.setMenuBar(self.menubar) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar) self.actionsetting = QtWidgets.QAction(MainWindow) self.actionsetting.setObjectName("actionsetting") self.actionexit = QtWidgets.QAction(MainWindow) self.actionexit.setObjectName("actionexit") self.actiondocument_2 = QtWidgets.QAction(MainWindow) self.actiondocument_2.setObjectName("actiondocument_2") self.actionabout = QtWidgets.QAction(MainWindow) self.actionabout.setObjectName("actionabout") self.menusetting.addSeparator() self.menusetting.addAction(self.actionsetting) self.menusetting.addSeparator() self.menusetting.addAction(self.actionexit) self.menuhelp.addAction(self.actiondocument_2) self.menuAbout.addAction(self.actionabout) self.menubar.addAction(self.menusetting.menuAction()) self.menubar.addAction(self.menuhelp.menuAction()) self.menubar.addAction(self.menuAbout.menuAction()) self.retranslateUi(MainWindow) self.w_key.setCurrentIndex(0) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): #重新加载界面 _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) self.w_time_signalture.setItemText(0,_translate("MainWindow","4/4")) self.w_time_signalture.setItemText(1, _translate("MainWindow","3/4")) self.w_mode.setItemText(0, _translate("MainWindow", "major")) self.w_mode.setItemText(1, _translate("MainWindow", "dorian")) self.w_mode.setItemText(2, _translate("MainWindow", "phrygian")) self.w_mode.setItemText(3, _translate("MainWindow", "lydian")) self.w_mode.setItemText(4, _translate("MainWindow", "mixolydian")) self.w_mode.setItemText(5, _translate("MainWindow", "minor")) self.w_mode.setItemText(6, _translate("MainWindow", "locrian")) self.w_mode.setItemText(7,_translate("MainWindow","major pentatonic")) self.w_mode.setItemText(8,_translate("MainWindow","minor pentatonic")) self.w_bass.setItemText(0, _translate("MainWindow", "None")) self.w_bass.setItemText(1, _translate("MainWindow", "1")) self.w_bass.setItemText(2, _translate("MainWindow", "2")) self.w_bass.setItemText(3, _translate("MainWindow", "3")) self.w_bass.setItemText(4, _translate("MainWindow", "4")) self.w_bass.setItemText(5, _translate("MainWindow", "5")) self.w_bass.setItemText(6, _translate("MainWindow", "6")) self.w_bass.setItemText(7, _translate("MainWindow", "7")) self.w_accompany.setItemText(0, _translate("MainWindow", "1")) self.w_accompany.setItemText(1, _translate("MainWindow", "2")) self.w_accompany.setItemText(2, _translate("MainWindow", "3")) self.w_accompany.setItemText(3, _translate("MainWindow", "4")) self.w_accompany.setItemText(4, _translate("MainWindow", "5")) self.w_accompany.setItemText(5, _translate("MainWindow", "6")) self.w_key.setCurrentText(_translate("MainWindow", "C")) self.w_key.setItemText(0, _translate("MainWindow", "C")) self.w_key.setItemText(1, _translate("MainWindow", "D")) self.w_key.setItemText(2, _translate("MainWindow", "E")) self.w_key.setItemText(3, _translate("MainWindow", "F")) self.w_key.setItemText(4, _translate("MainWindow", "G")) self.w_key.setItemText(5, _translate("MainWindow", "A")) self.w_key.setItemText(6, _translate("MainWindow", "B")) self.label.setText(_translate("MainWindow", "Chord progression")) self.w_play.setText(_translate("MainWindow", "GO")) self.label_7.setText(_translate("MainWindow", "repeat")) self.w_repeat.setText(_translate("MainWindow", "1")) self.w_bpm.setText(_translate("MainWindow", "120")) self.w_chord_progression.setText(_translate("MainWindow","4321")) self.label_6.setText(_translate("MainWindow", "bpm")) self.label_5.setText(_translate("MainWindow", "key")) self.label_3.setText(_translate("MainWindow", "intensity")) self.label_2.setText(_translate("MainWindow", "Time signalture")) self.label_4.setText(_translate("MainWindow", "mode")) self.label_8.setText(_translate("MainWindow", "accompany")) self.label_9.setText(_translate("MainWindow", "bass")) self.checkBox.setText(_translate("MainWindow", "silent")) self.menusetting.setTitle(_translate("MainWindow", "Menu")) self.menuhelp.setTitle(_translate("MainWindow", "Help")) self.menuAbout.setTitle(_translate("MainWindow", "About")) self.actionsetting.setText(_translate("MainWindow", "setting")) self.actionexit.setText(_translate("MainWindow", "exit")) self.actiondocument_2.setText(_translate("MainWindow", "document")) self.actionabout.setText(_translate("MainWindow", "about"))
通过MainCode实现的继承关系如图所示,其中Impromptu类
为可运行的脚本,Ui_MainWindow
为PyQt5。
#完成基础页面布局后,对事件设定触发,并定义触发的函数 import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox from PyQt5.QtCore import QCoreApplication from my import v4 #import _thread #import threading import all1 play = False class MainCode(all1.Impromptu, QMainWindow, v4.Ui_MainWindow): def __init__(self): #初始化 super().__init__() QMainWindow.__init__(self) v4.Ui_MainWindow.__init__(self) #super().setupUi(self) self.setupUi(self) self.w_play.clicked.connect(self.go) self.w_key.activated[str].connect(self.set_key) self.w_mode.activated[str].connect(self.set_mode) self.w_bass.activated[str].connect(self.set_bass) self.w_accompany.activated[str].connect(self.set_accompany) self.w_time_signalture.activated[str].connect(self.set_time_signalture) self.actionexit.triggered.connect(QCoreApplication.instance().quit) self.actiondocument_2.triggered.connect(self.document) self.actionsetting.triggered.connect(self.setting) self.actionabout.triggered.connect(self.about) def document(self): #定义文本 text = "this is ducoment" QMessageBox.information(self, "Message", text, QMessageBox.Ok) def setting(self): #定义设置 text = "this is setting" QMessageBox.information(self, "Message", text, QMessageBox.Ok) def about(self): #定义关于 text = "author: @dongmie1999\n2020.4" QMessageBox.information(self, "Message", text, QMessageBox.Ok) def set_key(self, text): #定义设置键 self.key = text def set_mode(self, text): #定义设置模式 self.mode = text def set_bass(self, text): #定义设置贝斯 if text == 'None': self.sw_bass = False else: self.sw_bass = True self.bass_type = int(text) def set_accompany(self, text): #定义设置伴奏 self.accompany_type = int(text) def set_time_signalture(self, text): #定义设置时间 self.time_signalture = text def go(self): #定义行进 try: self.bpm = int(self.w_bpm.text()) except ValueError: text = "bpm should be an positive integer.\nrecommend: 70~150" QMessageBox.information(self, "Message", text, QMessageBox.Ok) return try: if not 0 < int(self.w_repeat.text()) <20: raise ValueError except ValueError: text = "repeat should be an positive integer.\nrecommend: 1~5" QMessageBox.information(self, "Message", text, QMessageBox.Ok) return self.intensity = int(self.w_intensity.value()/100) try: #用级数表示和弦 for t in self.w_chord_progression.text(): if 0 < int(t) < 8: pass else: text = "Input should be a series fo numbers.\nEach number must be between 1~7.\n" + \ "Example: 4321 or 4536251 or 1645" QMessageBox.information(self, "Message", text, QMessageBox.Ok) return self.chord_progression = self.w_chord_progression.text() except ValueError: #和弦名称 self.chord_progression=self.w_chord_progression.text().split(',') #print(self.checkBox.checkState()) if self.checkBox.checkState(): self.silent = True else: self.silent = False print("Song making...") self.write_song() self.mid.save_midi() print("Done. Start to play.") #for n in range(self.repeat): #获取条目文本 #str_n = 'File index{0}'.format(n) #添加文本到列表控件中 #self.listFile.addItem(str_n) #实时刷新界面 #QApplication.processEvents() #睡眠1秒 #time.sleep(1) #thread.start_new_thread(self.mid.play_it()) self.mid.play_it() if __name__ == '__main__': #主函数 app = QApplication(sys.argv) md = MainCode() md.show() #t1 = threading.Thread(target=md.show()) #t2 = threading.Thread(target=md.go()) #t1.start() #t1.join() #if play: #print("play") #t2.start() #t2.join() #play = False sys.exit(app.exec_())
GUI界面选择的和弦级数都是调内和弦。在Impromptu中使用self.chord_progression = [Cmaj7','Am7', 'F', 'E7']
, 可以使用调外和弦,给歌曲引入更丰富的和声。
运行主程序后显示GUI界面如图所示。用户可选择的歌曲信息有主音、调式、bpm、拍号、钢琴伴奏类型、贝斯伴奏类型、和弦进行、重复次数和即兴的强度,另外还有右下角音色的切换(普通/安静)。
由于即兴的特点,每次运行产生的音频都不同,此处展示C大调下和弦进行为4321,重复次数为1,贝斯和钢琴伴奏类型均为类型1的歌曲可视化结果,如图3所示。钢琴伴奏位于中音区(C3~4) 。即兴旋律可视化结果如图4所示,即兴旋律位于高音区(C4~C5) 。贝斯伴奏可视化结果如图5所示,即兴旋律位于低音区(C1~C2)。
如果大家想继续了解人工智能相关学习路线和知识体系,欢迎大家翻阅我的另外一篇博客《重磅 | 完备的人工智能AI 学习——基础知识学习路线,所有资料免关注免套路直接网盘下载》
这篇博客参考了Github知名开源平台,AI技术平台以及相关领域专家:Datawhale,ApacheCN,AI有道和黄海广博士等约有近100G相关资料,希望能帮助到所有小伙伴们。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。