用Python演奏《国际歌》

文章目录

  • 一、背景与需求
  • 二、必备知识
    • 2.1 Python生成音乐的原理
    • 2.2 十二平均律
    • 2.3 简单乐理知识
  • 三、实现
    • 3.1 对琴谱进行编码
    • 3.2 Music类
    • 3.3 Staff类
    • 3.4 Converter类
  • 四、项目代码
  • 五、不足与展望

一、背景与需求

前一段时间在B站看到一个视频,up主用matlab演奏出了《小星星》这首歌1。当时我就在想,python能不能做到这一点呢?《小星星》的谱子太简单了,我决定尝试演奏《国际歌》

二、必备知识

2.1 Python生成音乐的原理

想要生成一段音乐,首先要了解声音产生的原理:

声音是由物体振动产生的声波。是通过介质(空气或固体、液体)传播并能被人或动物听觉器官所感知的波动现象。最初发出振动(震动)的物体叫声源。声音以波的形式振动(震动)传播。声音是声波通过任何介质传播形成的运动。
声音是一种波。可以被人耳识别的声(频率在20 Hz~20000 Hz之间),我们称之为声音。

想要用Python生成一段音乐,道理其实非常简单。只要我们想办法生成这段音乐的波形数据,再将其写入文件保存,就可以播放了。

怎样生成一段声波呢?这可以通过Numpy库实现。

下面的代码用于生成一段正弦波并可视化:

import numpy as np 
import matplotlib.pyplot as plt# 生成一条正弦波
x = np.linspace(0, 2*np.pi, 8192)
y = np.sin(x)
#可视化
plt.plot(x,y) 
plt.show()

可视化结果如图所示:
正弦波

当然,如果我们把这样一段正弦波写入文件并播放,是不会听到任何声音的。这是因为人耳可以听到的声音的频率范围在20到2万赫兹(Hz)之间,而上面这条正弦波的频率为1Hz,属于人耳听不到的次声波。

声波
接下来这段代码用于生成一条频率为440Hz的正弦波并可视化:

import numpy as np 
import matplotlib.pyplot as pltfrequency = 440  
x = np.linspace(0, 2*np.pi, 8192) 
y = np.sin(frequency * x)
plt.plot(x,y) 
plt.show()

结果如下:
440Hz
由于频率太大,生成的波周期太短,水平方向挤在了一起。我们不妨将图片放大看一看:
440Hz放大
如果将这条正弦曲线写入文件并播放,我们就可以听到声音了。这条440Hz的波对应的正是中央A2,也就是我们常说的la。

2.2 十二平均律

十二平均律3
(英语:Equal temperament),又称十二等程律,音乐律式的一种,也是当今最主流的律式。将一个八度平均分成十二等份,每等分称为半音,音高八度音指的是频率乘上二倍。八度音的频率分为十二等分,即是分为十二项的等比数列,也就是每个音的频率为前一个音的2的12次方根:

十二平均律
根据此表,我们可以计算出各个音符所对应的频率,从而生成对应的波。

2.3 简单乐理知识

想要用Python演奏国际歌,首先要对琴谱进行编码,将五线谱输入计算机中。这要求我们必须对乐理知识具备一定的了解。这里不作详细介绍,可参考 乐理知识以及musicXml属性介绍 这篇文章。

三、实现

3.1 对琴谱进行编码

我手里现有的国际歌琴谱是五线谱形式的。
国际歌五线谱
然而,计算机可不识谱。为了让它能理解琴谱的内容,我们必须首先对琴谱进行编码。这里我自行制定了一种编码格式,并手工对其进行了编码:
国际歌编码
其中每个section表示一个小节,每个音符用一个3维向量表示。如[0,1,3]表示一个持续3拍的中央C的do.

3.2 Music类

Music类用来实例化一首歌。其包含以下成员:

主要变量

  • name : 一个字符串,歌曲名称
  • staff : 一个Staff对象,歌曲的五线谱。
  • wave : np.array()数组,五线谱对应的波形。
  • Fs : 采样率
  • converter : 一个Converter对象,用来实现五线谱到波形的转换。

主要函数

  • load_staff(self,file_name) : 读取编码后的五线谱。
  • update_music(self) : 根据读取的五线谱更新波形。
  • save_music(self,file_name,file_type="wav") : 保存音频文件。
  • draw_wave(self) : 画出波形图。
class Music():def __init__(self, name=""):"""Init an instance of MyMusic.Parameters----------name : str, [optional]Name of the music. Must be str."""#Judge the type of input.assert name is None or isinstance(name, str)self.__name = nameself.__staff = Noneself.__wave = np.array([])self.__Fs = 8192self.__converter = Converter(self.__Fs)def load_staff(self,file_name):"""Load the staff from a file.Parameters----------file_name : strThe file to read from.Returns-------state : boolWhether loaded the data successfully."""#Judge the input.assert isinstance(file_name, str), "file_name must be a string"assert len(file_name.split(".")) == 2, "Input error. \neg.'start.txt'"self.__staff = Staff(file_name)self.update_music()def save_staff(self,file_name):"""Save the staff to a file.Parameters----------file_name : strThe file to save to.Returns-------state: boolWhether saved the data successfully."""def update_music(self):"""Update the music(wave) by self.__staff."""self.__wave = self.__converter.gen_music(self.__staff)def save_music(self,file_name,file_type="wav"):"""Save the music as an audio.Parameters----------file_name : strThe file to save to.file_type : strType of the file to save to. Defaults to 'wav'and it means save the audio as "{file_name}.wav".Returns-------state : boolWhether successfully saved the music as an audio."""path = file_name.split(".")[0]+"."+file_typesf.write(path, self.__wave, self.__Fs, 'PCM_24')def play(self):"""Play the music."""def draw_wave(self):"""Draw the waveform according to self.__wave."""n = len(self.__wave)     # Number of samples.t = n/self.__Fs          # Range of t is [0,t]x = np.linspace(0, t, n)plt.plot(x,self.__wave)  # Draw the wave.plt.show()

3.3 Staff类

Staff类用于表示琴谱

主要变量

  • rythm : 琴谱的节奏。
  • loop : 储存琴谱循环信息。

主要函数

  • read(self) : 读取编码后的五线谱。
class Staff():def __init__(self, file_name):"""Init an instance of a Staff.Parameters----------f_name : strThe name of the file.f_type : strThe type of the file."""self.__sections = []self.__name ,self.__type = file_name.split(".")self.__text = ""self.__rythm = 60self.__loop = Noneself.__supported_type = {"txt"}            # The type of file supported.self.read()    # Read file.def read(self):"""Init the staff from a file."""assert self.__type in self.__supported_type, "Sorry, this type of file is not supported."if self.__type == "txt":self.read_from_txt()def read_from_txt(self):"""Load the staff from a txt file.Parameters----------file_name : str ("xxx.txt")The full name of the file."""file_name = self.__name + "." + self.__typewith open(file_name, "r") as file:self.__text = file.read()re_rythm = re.compile("rythm=([0-9]*)",re.S)re_loop = re.compile("loop=(.*?\))",re.S)re_section = re.compile("(<section.*?>.*?</section>)",re.S)re_section_att = re.compile("<section (.*?)>(.*)</section>",re.S)self.__rythm = int(re_rythm.findall(self.__text)[0])  # Find the rythm.self.__loop = eval(re_loop.findall(self.__text)[0])sections = re_section.findall(self.__text)       # Find all sections.for section in sections:# Create a temp dict to save the information of this section.dict_att = {}# Find the attributions and the notes of this section.match = re_section_att.findall(section)# Add every attribute to `dict_att`.attributes = match[0][0].split()for att in attributes:key, value = att.split("=")dict_att[key] = value# Create a list `notes` and add every note to this list.notes_temp = match[0][1].split("\n")notes = []for i in range(len(notes_temp)):note = notes_temp[i].strip(" ")note = note.strip("\t")if note:notes.append(note)# Create a dict to save the information of this section, and add it to `self.__section`.self.__sections.append({"attribute":dict_att, "notes":notes})# print(self.__sections)@propertydef sections(self):return self.__sections@propertydef rythm(self):return self.__rythm@propertydef loop(self):return self.__loop

3.4 Converter类

Converter类用于实现五线谱到波形的转换。

主要变量

  • Fs : 采样率。

主要函数

  • read(self) : 读取编码后的五线谱。
  • get_frequency(self,major,scale) :计算给定音符的频率。
  • get_wave(self,major,scale,rythm=1) : 生成给定音符的波形。
  • gen_music(self,staff) : 拼接各音符的波形,生成整首音乐的波形。
class Converter():def __init__(self, Fs=8192):"""Init an instance of Converter.Parameters----------Fs : int [optional]The sampling rate."""self.__Fs = Fsdef get_frequency(self,major,scale):"""Calculate the Frequency.Parameters----------major : intThe major. For example, when it takes 0, it means C major.scale : int, range[-1,12]The scale. -1 means a rest, and 1 means "do", 12 means "si".Returns-------frequency : floatThe Frequncy calculated by the major and scale."""frequency = 440*(2**major)frequency = frequency*2**((scale-1)/12)return frequencydef get_wave(self,major,scale,rythm=1):"""Generate the wave of a note.Parameter---------major : intThe major. For example, when it takes 3, it means C major.scale : int, range[-1,12]The scale. -1 means a rest, and 1 means "do", 12 means "si".rythm : intThe rythm of the note. When it takes 1.5, int means 1.5 unit-time per beat.Returns-------y : np.arrayThe wave generated."""Pi = np.pix = np.linspace(0, 2*Pi*rythm, int(self.__Fs*rythm), endpoint=False) # time variable# When scale==-1, it means a rest.if scale == -1:y = x*0else:frequency = self.get_frequency(major, scale)y = np.sin(frequency*x)*(1-x/(rythm*2*Pi))return ydef gen_music(self,staff):"""Play a piece of music based on section.Parameters----------staff : class Staff.Returns-------wave : np.arrayThe wave of the staff."""sections = staff.sectionstime = 60/staff.rythmloop_start, loop_end, loop_times, loop_sub = staff.loopwave = np.array([])section_wave_ls = []for section in sections:notes = section["notes"]wave_list = []for line_str in notes:line = [eval(note) for note in line_str.split()]line_wave = np.array([])for note in line:major, scale, rythm = noterythm *= timey = self.get_wave(major, scale, rythm)line_wave = np.concatenate((line_wave,y),axis=0)wave_list.append(line_wave)length = min([len(line_wave) for line_wave in wave_list])section_wave = wave_list[0][:length]for i in range(1,len(wave_list)):section_wave += wave_list[i][:length]# wave = np.concatenate((wave,section_wave),axis=0)section_wave_ls.append(section_wave)temp = [w for w in section_wave_ls[:loop_start-1]]for i in range(loop_times):for w in section_wave_ls[loop_start-1:loop_end]:temp.append(w)if loop_sub:section_wave_ls = temp[:-1] + [w for w in section_wave_ls[loop_end:]]else:section_wave_ls = temp + [w for w in section_wave_ls[loop_end:]]for w in section_wave_ls:wave = np.concatenate((wave,w), axis=0)return wave

四、项目代码

完整代码已经上传至github,传送门: Python演奏国际歌

五、不足与展望

  1. 对琴谱的编码方式有待优化。我采用的方法太过粗糙,只提取了原谱最关键的少量信息。此外,由于编码规则是我自己指定的,这种编码也缺乏通用性。接下来可以尝试MusicXML4编码。
  2. 琴谱编码需要人工进行,费时费力,后期可以采用图像识别技术自动识别并编码琴谱。
  3. 后期可以再写一个UI界面,提高用户体验度。

  1. 【Matlab番外篇】怎样用Matlab生成一段音乐 ↩︎

  2. 维基百科:A(音名) ↩︎

  3. 维基百科:十二平均律 ↩︎

  4. Songs and Schemas ↩︎

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在线钢琴应用AutoPiano

什么是 AutoPiano &#xff1f; 自由钢琴&#xff08;AutoPiano&#xff09;是利用 HTML5 技术开发的在线钢琴应用&#xff0c;致力于为钢琴爱好者、音乐爱好者以及其他所有的创造者提供一个优雅、简洁的平台&#xff0c;在学习工作之余可以享受钢琴、音乐的美好。AutoPiano 还…

利用Python生成钢琴音色

在 csdn上看到一篇博客“根据乐谱合成钢琴音乐&#xff08;https://blog.csdn.net/u011478373/article/details/60470332&#xff09;”&#xff0c;写得不错&#xff0c;非常感兴趣&#xff0c;就把博客中的Python代码拷贝下来运行了一下&#xff0c;结果不行&#xff0c;原因…

前端作品-网页弹钢琴

点此查看 所有教程、项目、源码导航 文章目录 1. 作者简介2. 效果展示3. 源码下载 1. 作者简介 本网站作者田迅&#xff0c;是我的20级软件技术专业学生&#xff0c;爱好前端。擅长使用HTML、CSS、JS、jQuery、BootStrap等技术构建美观大气的网站&#xff0c;本文介绍其作品&a…

《科尼龙作曲家》——音乐课教学的绝佳工具

乔奥(Joao Carlos Ramalheiro&#xff0c;科思鹏的创始人)自己作为一名音乐老师&#xff0c;在教学的时候也经常需要用到五线谱工具。市面上虽然已经存在多款专业的五线谱工具软件&#xff0c;但是他发现&#xff0c;这些五线谱软件并不能很好地适应于音乐教学。它们功能繁多、…

编写程序实现乐手弹奏乐器。乐手可以弹奏不同的乐器从而发出不同的声音。 可以弹奏的乐器包括二胡、钢琴和琵琶。 实现思路及关键代码

编写程序实现乐手弹奏乐器。乐手可以弹奏不同的乐器从而发出不同的声音。 可以弹奏的乐器包括二胡、钢琴和琵琶。 实现思路及关键代码&#xff1a; 1)定义乐器类Instrument&#xff0c;包括方法makeSound() 2)定义乐器类的子类&#xff1a;二胡Erhu、钢琴Piano和小提琴Violin 3…

matlab钢琴音教程,手把手教你钢琴基本指法(有图有真相,通俗易懂,钢琴入门必备)...

示例图片&#xff1a; b、如果 2、3、4 指在白键上&#xff0c;那么 1 指在穿指后不可以落到黑键上。否则就是别马腿&#xff0c; 大家可以试一试。这是要说一下&#xff0c;1 指由于其生理构造&#xff0c;不太适合经常上黑键。但它在相 对少数的一些场合还是需要上黑键的。但…

怎样才能让计算机发出钢琴的按键声,这篇文章教你如何弹出钢琴好音色

原标题&#xff1a;这篇文章教你如何弹出钢琴好音色 怎样演奏好钢琴的音色 导读&#xff1a;在教学中往往容易陷于就事论事地去应付面临的各种视谱、节奏、速度、熟练、背谱等问题的解决之中&#xff0c;而无暇去顾及声音的质量&#xff0c;这恰恰是误区之所在。本文的重点即在…

用Unity的GetSpectrumData方法识别钢琴曲中的钢琴琴键

目录 要解决的问题音律五度相生律与 3 2 \frac{3}{2} 23​纯率与 5 4 \frac{5}{4} 45​十二平均律与 2 1 12 2^\frac{1}{12} 2121​88键钢琴各键键位与音高 Unity GetSpectrumData获取的音频数据与88键钢琴各键的映射全部代码与测试结果未解决的问题 要解决的问题 我在上一篇系…

国内智能音箱市场逐渐崛起,那么问题来了……

来源&#xff1a;AI锐见 “嗨&#xff0c;Alexa&#xff0c;放首歌。” 2014年&#xff0c;搭载了亚马逊虚拟语音个人助理Alexa的Echo面世&#xff0c;让人类同机器的交互方式出现了另一种可能。 苹果联合创始人Steve Wozniak就曾在2016年表示&#xff0c;智能语音交互将成为计…

前途未卜的智能音箱,语音助手还差一个杀手级应用

作者 | Rani Molla 译者 | 刘旭坤 整理 | 非主流、Jane 出品 | AI科技大本营 近日&#xff0c;亚马逊推出了一款可以用语音控制的智能微波炉。至此&#xff0c;亚马逊推出的由 Alexa 语音控制的智能家居产品的数量已累计超过十种。由此&#xff0c;我们可以看到亚马逊推广自家语…

当你问智能音箱问题的时候,它会回答你什么?

笔者测试了25个不同类型的问题&#xff0c;比较了小爱mini和小度在家的回答&#xff0c;借此设计声学测试的问题&#xff0c;问题、问题类型、期望回复以及音箱的真实回复如下。测试时间为2018年10月份左右。 对话服务 序号对话期望回复对话类型小爱mini小度在家1珠海明天天气…

热闹了一年的智能音箱,为什么说只是过渡?

IT派 - {技术青年圈} 持续关注互联网、大数据、人工智能领域 关注 往期 精彩回顾 重大改变&#xff01;Excel即将接入Python&#xff01;办公软件也要革命 2017年大数据领域薪资有多高&#xff1f; 支付宝的套路 行业热&#xff0c;行业洗牌&#xff0c;行业发展&#xff0c;下…

智能音箱对比:Google,Amazon,Apple,讯飞

智能音箱 好久没写Blog&#xff0c;最近主要也在关心异军突起的智能音箱行列&#xff0c;这里也来稍微的分析下。 前言 2014年&#xff0c;Amazon不声不响的推出了智能音箱Echo&#xff0c;并慢慢建立了Alexa语音服务平台&#xff0c;直到2016年销量忽然剧增达到了1000万台。消…

钉钉添加代收邮箱地址

如163邮箱添加到钉钉客户端代收&#xff0c;需要在163邮箱登录后在设置中添加协议&#xff0c;并获取验证密码&#xff0c;验证密码在代收客户端使用&#xff0c;代收客户端输入账号是原始账号&#xff0c;密码就是获取的验证密码

出口托收与进口代收

出口托收与进口代收 出口托收&#xff08;Ontward Documentary Collection&#xff09;与进口代收&#xff08;Import Documentary Collection&#xff09;同属于托收业务范畴&#xff0c;出口托收和进口代收均为商业银行的业务之一&#xff0c;如托收流程图来说&#xff0c;对…

用EXCEL表格发送物业催费短信

催费成功者都是英雄 &#xff08;一&#xff09;催费的困境 做为一名物业工作人员&#xff0c;你是否也遇到过这样的困境呢&#xff1f; 1、心理被一种潜在的恐惧所困扰着——业主肯定会拒交费用&#xff0c;“那个业主真难办&#xff0c;我根本没办法说服他&#xff0c;真不…

批量查询快递单号筛选出代收单号

查询多个快递还在一个一个查吗&#xff1f;费时又费力&#xff0c;今天小编教你使用一款查询工具“快递批量查询高手”轻松解决难题&#xff0c;有兴趣的接着看下去吧。 工具/原料 快递批量查询高手 多个快递单号 方法/步骤 打开软件点击【添加单号】将需要查询的单号复制到文…

开快递代收点挣钱吗?怎么盈利?

现在是网络时代&#xff0c;很多事情都是在网络上面进行的。由于人们已经习惯了这种生活&#xff0c;网购显得更加受欢迎。正是因为如此&#xff0c;快递行业越来越红火。很多人开始开快递代收点&#xff0c;这样是否挣钱呢? 一、挣钱吗? 首先农村快递代收点会和各个快递公…

(附源码)计算机毕业设计SSM快递代收系统

&#xff08;附源码&#xff09;计算机毕业设计SSM快递代收系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xf…

多多买菜代收被叫停?拼多多这样回应

最近&#xff0c;有媒体报道称“多多买菜快递代收业务未取得经营许可被叫停”。在舆论不断发酵的情况下&#xff0c;拼多多方面对此做出了回应&#xff1a;拼多多并未在多多买菜所有站点开展快递代收业务&#xff0c;而是为有快递业务资质的站点提供“多多买菜”代收服务系统。…