基于Python++PyQt5马尔科夫模型的智能AI即兴作曲—深度学习算法应用(含全部工程源码+测试数据)

news2024/12/24 0:12:41

目录

  • 前言
  • 总体设计
    • 系统整体结构图
    • 系统流程图
  • 运行环境
    • Python 环境
    • PC环境配置
  • 模块实现
    • 1. 钢琴伴奏制作
      • 1)和弦的实现
      • 2)和弦级数转为当前调式音阶
      • 3)根据预置节奏生成伴奏
    • 2. 乐句生成
      • 1)添加音符
      • 2)旋律生成
      • 3)节奏生成
    • 3. 贝斯伴奏制作
      • 1)添加贝斯轨
      • 2)预置贝斯轨
    • 4. 汇总歌曲制作
      • 1)日志模块
      • 2)音乐可视化
      • 3)Impromptu类
    • 5. GUI设计
      • 1)用户界面空间初始化
      • 2)将控件绑定功能(信号与槽的绑定)
  • 系统测试
  • 工程源代码下载
  • 其它资料下载


在这里插入图片描述

前言

本项目旨在实现上下文相关的即兴演奏音乐生成。用户可以通过选择主音调式和输入歌曲信息,如BPM(每分钟节拍数)、弦级数和重复次数等参数,来定制所要生成的音乐。

用户首先选择主音调式,这将为生成的音乐设定一个基本的音乐调性。接着,用户输入歌曲信息,包括BPM、弦级数和重复次数等参数,这些参数将决定音乐的节奏和结构。

基于用户选择的主音调式和输入的歌曲信息,项目将通过算法生成上下文相关的即兴演奏音乐。音乐的旋律、和弦进行和节奏等元素将根据用户输入的参数进行自动调整,以符合用户的要求。

通过这个项目,用户可以根据自己的喜好和需求,定制个性化的即兴演奏音乐。无需专业音乐知识,用户只需选择主音调式和输入简单的歌曲信息,便可以生成符合自己心情和风格的音乐作品。这为音乐创作和欣赏带来了更多的自由和乐趣。

总体设计

本部分包括系统整体结构图和系统流程图。

系统整体结构图

系统整体结构如图所示。

在这里插入图片描述

系统流程图

伴奏制作、即兴旋律生成的流程如图1所示,贝斯模块及GUI (Graphical User Interface,图形用户接口)制作流程如图2所示。

在这里插入图片描述

图1 伴奏制作、即兴旋律生成的流程

在这里插入图片描述

图2 贝斯模块及GUI制作流程

运行环境

本部分包括Python环境和PC环境配置。

Python 环境

需要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

PC环境配置

本程序最终将打包成.exe,在Windows环境下运行。安装能读取并播放MIDI文件程序的用户可自行查看生成文件;反之,在选项卡中选择“自动播放”选项播放生成的MIDI文件。

模块实现

本项目包括5个模块:钢琴伴奏制作、乐句生成、贝斯伴奏制作、汇总歌曲制作和GUI设计,下面分别给出各模块的功能介绍及相关代码。

1. 钢琴伴奏制作

用户选择调式、输入和弦级数后,首先,将和弦级数转换为和弦名称;其次,用musthe将和弦名称转换为对应音;最后,根据用户选择的预置节奏型向MIDI中的钢琴轨写入钢琴伴奏。

1)和弦的实现

本部分代码完成添加和弦功能。

#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  #设第一排为重拍

2)和弦级数转为当前调式音阶

将用户输入的和弦级数(数字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

3)根据预置节奏生成伴奏

本项目为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)()

2. 乐句生成

使用hmmlearn,利用马尔可夫模型生成旋律和节奏。其中旋律表示为数字1~7,加上0和-1,1~7对应音阶中第1~7音(五声音阶对应五个音),0对应休止符,-1对应延音音符;节奏表示为数字1、2、4、8、16、32、 6、12、24,分别表示全音符、二分音符、四分音符、八分音符等。

1)添加音符

音轨内添加一个普通音符/休止符/延音音符的相关代码如下:

#向音轨内添加一个普通音符,此函数由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)

2)旋律生成

相关代码如下:

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

3)节奏生成

相关代码如下:

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

3. 贝斯伴奏制作

为丰富曲目内容,预置14组贝斯供用户选择。

1)添加贝斯轨

向歌曲内添加贝斯轨的相关代码如下:

#向音轨内添加一个普通音符,此函数由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))

2)预置贝斯轨

根据日常演奏经验,对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)()

4. 汇总歌曲制作

完成钢琴伴奏制作、乐句生成模块后,使用类Impromptu调用并完成MIDI文件的写入与播放,并使用装饰器完成日志记录,函数piano_roll_test()实现音乐的可视化。

1)日志模块

制作歌曲时记录日志,在当前目录下生成日志文件。

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

2)音乐可视化

相关代码如下:

#将音乐的旋律高低与时值长短可视化,保存图片于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')

3)Impromptu类

后台所有功能汇总类,在此实现即兴曲目的信息输入,可以保存并播放文件,实现音乐可视化。

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

5. GUI设计

为方便用户交互,使用PyQt5 Designer拖动控件,设计图形界面,使用PyQt-tools将制作好的.ui文件转为.py代码,完成用户界面初始化,再将控件绑定对应功能。

1)用户界面空间初始化

此代码由制作的.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"))

2)将控件绑定功能(信号与槽的绑定)

通过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)。

在这里插入图片描述

图3 钢琴伴奏可视化

在这里插入图片描述

图4 即兴旋律可视化

在这里插入图片描述

图5 贝斯伴奏可视化

工程源代码下载

详见本人博客资源下载页


其它资料下载

如果大家想继续了解人工智能相关学习路线和知识体系,欢迎大家翻阅我的另外一篇博客《重磅 | 完备的人工智能AI 学习——基础知识学习路线,所有资料免关注免套路直接网盘下载》
这篇博客参考了Github知名开源平台,AI技术平台以及相关领域专家:Datawhale,ApacheCN,AI有道和黄海广博士等约有近100G相关资料,希望能帮助到所有小伙伴们。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/842886.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Llama2 评测大公开!知识库场景下能否赶超 ChatGPT?

AIGC 人狂喜&#xff01;最近&#xff0c;Meta AI 发布了大语言模型 Llama2&#xff0c;为大模型的开发者注入了一剂强心针&#xff0c;因为无论从其灵活性、竞争力还是便捷性来看&#xff0c;都有不小的优势。 具体来看&#xff1a; Llama2 为开源产品且可免费商用&#xff0c…

【Spring AOP】什么是AOP

文章目录 1、AOP思想2、AOP入门案例3、AOP工作流程4、AOP切入点表达式5、AOP的五种通知类型6、AOP通知获取数据7、案例&#xff1a;百度网盘密码数据兼容处理8、AOP总结 1、AOP思想 AOP&#xff0c;即Aspect Oriented Programming&#xff0c;面向切面编程。是一种编程范式&am…

8.18信号量Semaphore和CountDownLatch

一 .Semaphore: 1.Semaphore是一个计数器(变量),描述可用资源的个数,用来判断是否有临界资源可用. 2.临界资源:多个进程或线程并发执行的实体可以公共使用到的资源. 3.pv操作:p操作(accquire(申请))表示计数器减一,v操作(release(释放))表示计数器加一. 4.锁是特殊的信号量…

RISC-V公测平台发布:如何在SG2042上玩转OpenMPI

About HS-2 HS-2 RISC-V通用主板是澎峰科技与合作伙伴共同研发的一款专为开发者设计的标准mATX主板&#xff0c;它预装了澎峰科技为RISC-V高性能服务器定制开发的软件包&#xff0c;包括各种标准bencmark、支持V扩展的GCC编译器、计算库、中间件以及多种典型服务器应用程序。…

[webpack] 处理样式 (二)

文章目录 1.介绍2.处理 Css 资源2.1 导入包2.2 功能配置2.3 添加 Css 资源 3.处理 Less 资源3.1 导入包3.2 功能配置3.3 添加 Less 资源 4.处理 Sass 和 Scss 资源4.1 导入包4.2 配置4.3 添加 Sass 资源4.4 运行webpack 5.处理 Styl 资源5.1 导入包5.2 配置5.3 添加 Styl 资源5…

微信开发之自动同意好友的技术实现

简要描述&#xff1a; 同意添加好友 请求URL&#xff1a; http://域名地址/acceptUser 请求方式&#xff1a; POST 请求头Headers&#xff1a; Content-Type&#xff1a;application/jsonAuthorization&#xff1a;login接口返回 参数&#xff1a; 参数名必选类型说明…

Jmeter学习和一个关于jmeter获取X-XSRF-TOKEN时的坑

Jmeter学习和一个关于jmeter获取X-XSRF-TOKEN时的坑 现在想对一个接口做性能测试&#xff0c;需要测试它多个线程并发下的调用 1.新建测试计划和线程组 略 2.新建http接口 一个完整的http接口包含请求头和请求&#xff0c;这里就需要两个组件&#xff1a;HTTP request、HT…

c语言函数作为形参的注意事项

1、c语言数组作为形参会退化成数组指针 #include "stdio.h" #include <stdlib.h>//数组作为函数的形参会退化成指针 void print_arr(int a[5]);// int *b int main() {int arr[5] { 1,2,3,4,5 };print_arr(arr);return 0; }void print_arr(int a[5]) {printf…

SocialFi 的开发中如何利用 NFTScan API 获取 NFT 数据

SocialFi 作为社交媒体与 Web3 的创新融合&#xff0c;致力于构建更加开放去中心化的社交平台。它赋能用户拥有数据控制权、实现内容价值&#xff0c;并通过代币经济建立起激励与治理机制&#xff0c;这正是 Web3 社交的独特魅力所在。SocialFi 为我们描绘了一个更加用户驱动、…

从0开始全栈深度学习工程师之路(四):VSCode提效设置和插件

在从0开始深度学习工程师之路&#xff08;三&#xff09;&#xff1a;Python开发环境搭建&#xff08;VSCode) 中,我们一步步搭建了基于VSCode的开发环境&#xff0c;这一篇我们继续优化我们的开发环境&#xff0c;毕竟工欲善其事&#xff0c;必先利其器。 配置 同步设置 我…

GG修改器安装与Root环境的安装

关于GG修改器大家应该都有一定的了解吧&#xff0c;就是类似于电脑端CE的一个软件。 GG修改器在百度云盘里请自行下载&#xff01; 百度网盘链接&#xff1a;https://pan.baidu.com/s/1p3KJRg9oq4s0XzRuEIBH4Q 提取码&#xff1a;vuwj 那我要开始了&#xff01; 本来不想讲GG…

WMS仓储管理系统的价值从哪些方面体现

仓库管理在不断改善&#xff0c;采用信息化来管理仓库。自从使用了WMS仓储管理系统解决方案后&#xff0c;仓库管理的效率大大提高了&#xff0c;而且WMS系统有很多价值。 首先&#xff0c;WMS系统可以帮助员工更省时省力地完成仓库管理工作&#xff0c;提高效率和准确性。传统…

农业大数据可视化平台,让农业数据更直观展现!

农业大数据可视化平台是指利用大数据技术和可视化工具&#xff0c;对农业领域的数据进行收集、整理、分析和展示的平台。它可以帮助农业从业者更好地理解和利用农业数据&#xff0c;提高农业生产效率和决策水平。 农业大数据可视化平台通常具有以下特点和功能&#xff1a; 数据…

WorkTool企微机器人自动接收图片回传(方案三)

自动接收图片并上传到服务器&#xff0c;仅适用企业微信应用 前言 WorkTool企微机器人可以接收客户群的消息&#xff0c;但接收图片一直是个问题&#xff0c;前面也介绍过两种图片接收方案&#xff0c;但都会影响运行效率&#xff0c;并且不能达到100%的图片接收率&#xff0…

配置两台数据库为主从数据库模式

一、主库配置 1、修改配置文件 /etc/my3306.cnf #mysql服务ID&#xff0c;保证整个集群环境中唯一&#xff0c;默认为1server-id1#是否只读&#xff0c;1代表只读&#xff0c;0代表读写read-only0#忽略的数据&#xff0c;指不需要同步的数据库#binlog-ignore-dbmysql#指定同步…

Jmeter命令行运行实例讲解

1. 简介 使用非 GUI 模式&#xff0c;即命令行模式运行 JMeter 测试脚本能够大大缩减所需要的系统资 本文介绍windows下以命令行模式运行的方法。 1.1. 命令介绍 jmeter -n -t <testplan filename> -l <listener filename> 示例&#xff1a; jmeter -n -t test…

承泰科技Q3再获30多个智驾项目,新增订单0.86亿!累计近11亿!

中国毫米波雷达市场正处于高速发展期&#xff0c;以承泰科技为代表的本土供应商在前装量产赛道上展示出加速度。 高工智能汽车研究院预测&#xff0c;随着L2及L2持续处于市场增长的高速期&#xff0c;对应毫米波雷达上车量将在2023年实现30-50%的同比增速。 根据高工智能汽车…

Gpt微信小程序搭建的前后端流程 - 前端小程序部分-1.基础页面框架的静态设计(二)

Gpt微信小程序搭建的前后端流程 - 前端小程序部分-1.基础页面框架的静态设计(二) 在开始这个专栏&#xff0c;我们需要找一个小程序为参考&#xff0c;参考和仿照其界面&#xff0c;聊天交互模式。 这里参考小程序-小柠AI智能聊天&#xff0c;可自行先体验。 该小程序主要提供了…

轻量级容器管理工具 Containerd

1. 轻量级容器管理工具 Containerd 2. Containerd的两种安装方式 3. Containerd容器镜像管理 4. Containerd数据持久化和网络管理 1. 前言 早在2016年3月&#xff0c;Docker 1.11的Docker Engine里就包含了containerd&#xff0c;而现在则是把containerd从Docker Engine里彻底…

空地协同智能消防系统——无人机、小车协同

1 题目 1.1 任务 设计一个由四旋翼无人机及消防车构成的空地协同智能消防系统。无人机上安装垂直向下的激光笔&#xff0c;用于指示巡逻航迹。巡防区域为40dm48dm。无人机巡逻时可覆盖地面8dm宽度区域。以缩短完成全覆盖巡逻时间为原则&#xff0c;无人机按照规划航线巡逻。发…