菊花

用NumPyldquo弹奏rdqu

发布时间:2021/10/6 14:37:48   点击数:
北京白癜风医院哪家比较好 http://pf.39.net/bdfyy/
1前言大家好,欢迎来到Crossin的编程教室!在Python的世界里,没有一个模块能够像NumPy那样支撑并影响着整个生态系统:从科学计算到数据处理,从视觉识别到机器学习,从神经网络到虚拟现实,处处都有它的身影。无论是OpenCV、OpenGL,还是Pandas、Matplotlib,抑或是Scikti-learn、TensorFlow、Keras、Theano、PyTorch,无不依赖于NumPy,尤其是依赖它所创造的数组对象(numpy.ndarray)。NumPy几乎无所不能。NumPy最广为人知的能力是图像处理,而它最基础的应用是科学计算。本文则是独辟蹊径,讨论如何使用NumPy发出声音,以及如何模拟吉他音色,最终生成CD级别的音乐文件。除了生成wave文件时用到标准模块wave,全部代码仅依赖NumPy单个模块。此外,采集吉他音频数据(供频谱分析用)和播放wave文件时,还用到了PyAudio模块。如果有同学想重复频谱分析、使用代码播放wave文件,请使用如下的命令安装PyAudio模块。

pipinstallPyAudio2发出单一频率的声音想用NumPy弹奏吉他,先得让NumPy发出声音,比如,生成频率为Hz(吉他3弦空弦音的频率)且持续3秒钟的声音。这需要3x=个周期的波形。假定采样频率为22.05kHz(相当于调频收音机的音质),3秒钟会采集到个数据。如果每个数据用两个字节的整数表示,则声音幅度的动态范围从-到。以下代码在[0,π]之间均匀采样点,求其正弦值,逐点连线,即可得到频率为Hz、持续时间3秒、量化精度16位、采样频率为22.05kHz的正弦波。

importnumpyasnpimportmatplotlib.pyplotaspltx=np.linspace(0,*np.pi,,endpoint=False)y=np.sin(x)*y.dtypedtype(float64)y=y.astype(np.int16)y.dtypedtype(int16)plt.plot(x,y,color=green)plt.show()下图左侧画出了全部个点,图像变成绿色块了;右侧只画出了前个点,可以看出清晰的正弦波形。现在,声波有了,怎样才能听到声音呢?用PyAudio模块可以直接在声卡上播放这个数据(后面的代码有实现),也可以借助于Python的标准模块wave将声波数据保存为wave文件。wave模块是一个读写后缀名为.wav文件的工具模块,用起来非常简单。

importwavewithwave.open(rd:\sound_Hz_3s.wav,wb)asfp:fp.setparams((1,2,,0,NONE,NONE))fp.writeframes(y.tobytes())去D盘下看看,多了一个名为sound_Hz_3s.wav的文件。点击并播放它,就会听到类似轮船开动时的汽笛声。3模拟吉他音色刚才生成的声音,虽然和吉他3弦的空弦音频率相同,但听起来却一点不像吉他。那么吉他发出的声音是什么样的呢?下图是我用声卡采集的吉他1弦和2弦的空弦音的波形图,时长大约4秒钟左右。下面是采集声音的代码。有兴趣的同学可以拿出吉他或者其他乐器录制一段,然后做一下频谱分析。

importpyaudioimportnumpyasnpimporttimedefcapture(rate,chunk):pa=pyaudio.PyAudio()stream=pa.open(format=pyaudio.paInt16,#设置量化精度(每个采样数据占用的位数)channels=1,#设置单声道模式rate=,#设置采样频率frames_per_buffer=,#设置声卡读写缓冲区input=True#设置声卡输出模式)data=list()whilelen(data)50:data.append(np.frombuffer(stream.read(),dtype=np.int16))stream.close()pa.terminate()returnnp.hstack(data)if__name__==__main__:foriinrange(5):print(5-i)time.sleep(1)print(Start)data=capture(,)np.save(吉他10.npy,data)NumPy提供了快速傅里叶分析工具,可以分析刚才采集到的吉他声音的频率成分及其强度。

importnumpyasnpimportmatplotlib.pyplotaspltimporttimeimportwaveplt.rcParams[font.sans-serif]=[FangSong]plt.rcParams[axes.unicode_minus]=Falsed1=np.load(吉他10.npy)d2=np.load(吉他20.npy)plt.subplot()plt.plot(d1,c=g)plt.title(吉他1弦空弦音)plt.subplot()plt.plot(d2,c=m)plt.title(吉他2弦空弦音)#plt.show()fd1=np.fft.fft(d1[:])#截取吉他1弦空弦音1秒钟的数据进行傅里叶分析fd2=np.fft.fft(d2[:])#截取吉他2弦空弦音1秒钟的数据进行傅里叶分析plt.subplot()plt.plot(np.abs(fd1[:]/),c=g)plt.title(吉他1弦单边频谱图)plt.subplot()plt.plot(np.abs(fd2[:]/),c=m)plt.title(吉他2弦单边频谱图)plt.show()不难看出,吉他发出的声音有以下两个特点:

声音幅度在振动中逐渐变小

频谱显示存在较强幅度的二倍频、三倍频、四倍频

importnumpyasnpimportmatplotlib.pyplotaspltx=np.linspace(0,3*np.pi,,endpoint=False)y1=1-x/(10*np.pi)+(1-x/(6*np.pi))*np.sin(x)*0.5x=np.arange(3*)/y2=0.7*np.exp(-x)GUITAR_EFFECT_ARRAY=np.hstack((y1,y2))y_guitar=y*GUITAR_EFFECT_ARRAY[:y.shape[0]]plt.subplot()plt.plot(GUITAR_EFFECT_ARRAY)plt.subplot()plt.plot(y_guitar)plt.show()上面的代码,生成了一个模拟吉他波形的包络线,将等幅的正弦波模拟成吉他波形。效果如下图所示。解决了声波的振幅衰减,还需要叠加二倍频、三倍频、四倍频。对于NumPy来说,这都是小菜一碟。

importnumpyasnpimportmatplotlib.pyplotaspltplt.rcParams[font.sans-serif]=[FangSong]plt.rcParams[axes.unicode_minus]=Falsex1=np.linspace(0,4*np.pi,)x2=np.linspace(0,8*np.pi,)x3=np.linspace(0,12*np.pi,)y1=np.sin(x1)y2=np.sin(x2)y3=np.sin(x3)y=np.sum(np.dstack((y1,y2,y3))[0],axis=1)plt.subplot()plt.title(基波)plt.plot(y1,c=be)plt.subplot()plt.title(二倍频)plt.plot(y2,c=cn)plt.subplot()plt.title(三倍频)plt.plot(y3,c=y)plt.subplot()plt.title(复合波形)plt.plot(y,c=m)plt.show()不同频率的波形叠加在一起,效果如下图所示。4弹出吉他上所有的音符写到这里,必须要感谢中国古代的一位贵族——明朝皇室子弟朱载堉。他是一位被严重低估了的天才,他也是我认为最类似以解决数学问题为乐趣的欧洲贵族的中国人之一。他的主要学术成果是十二平均律,经过传教士利玛窦传到西方后,直接导致了西方现代音乐理论的兴起。朱载堉同时也是手工开方的先驱,他自制81档算盘开12次方,堪称前无古人后无来者。扯远了。如果理解十二平均律的话,很容易计算出吉他上所有品格的音频频率。简单说,同一根弦上,从低到高所有品格的频率是一个等比数列,比例为2的1/12次方。知道A=Hz的话,也很容易根据十二平均律计算出从1弦到6弦的频率分别为.6Hz、.9Hz、.0Hz、.8Hz、.0Hz、82.4Hz。这里定义1弦空弦用字符串10表示,1弦3品用字符串13表示,字符串则表示2弦12品,字符串0则表示休止。计算吉他上指定弦品音频频率的代码如下。

defget_frequency(pos):"""返回指定弦品pos的频率"""fs=(.6,.9,.0,.8,.0,82.4)ifpos[0]==0:return0else:returnfs[int(pos[0])-1]*pow(2,int(pos[1:])/12)5吉他谱的格式约定仅有吉他弦品的表示法还不够,还需要定义每个音符的时值表示。这里约定使用节拍数表示。如果以4分音符为1拍,则4表示全音符,2表示2分音符,1表示4分音符,0.5表示8分音符,0.25表示16分音符,依次类推。有了吉他弦品和时值的表示,如何描述吉他谱呢?这里约定,一个吉他谱是若干乐谱小节的有序集合,每个乐谱小节又是多个声部的有序集合,每个声部又是一根琴弦上品格和时值的元组的有序集合。这里说的有序集合,就是Pytho的列表。下面是根据这个约定翻译过来的“天空之城”的序曲部分。

castle_in_the_sky=[[#第1节[(10,0.5),(12,0.5)]#1弦],[#第2节[(13,1.5),(12,0.5),(13,1),(13,0.5),(17,0.5)],#1弦[(0,1),(20,3)],#2弦[(0,0.5),(30,2),(30,1.5)],#3弦[(60,2),(60,2)]#6弦],[#第3节[(12,4)],#1弦[(0,1),(23,2),(20,1)],#2弦[(0,0.5),(32,1),(32,2.5)],#3弦[(40,2),(40,2)]#4弦],[#第4节[(10,2),(10,1),(13,1)],#1弦[(21,1.5),(23,2.5)],#2弦[(0,1),(30,1.5),(30,1),(30,0.5)],#3弦[(0,0.5),(42,3.5)],#4弦[(53,4)]#5弦],[#第5节[(23,3),(20,1)],#2弦[(0,1),(30,1),(30,2)],#3弦[(0,0.5),(40,1),(40,2.5)],#4弦[(52,4)]#5弦],[#第6节[(0,2.5),(13,1.5)],#1弦[(21,1.5),(20,0.5),(21,2)],#2弦[(0,1),(30,2.5),(30,0.5)],#3弦[(0,0.5),(42,2.5),(42,1)],#4弦[(50,2),(50,2)]#5弦],[#第7节[(0,3),(13,0.5),(13,0.5)],#1弦[(20,2),(20,2)],#2弦[(0,1),(30,3)],#3弦[(0,0.5),(42,1),(42,2.5)],#4弦[(60,4)]#6弦],[#第8节[(12,3),(12,0.5),(10,0.5)],#1弦[(0,1.5),(22,0.5),(22,2)],#2弦[(0,1),(33,1.5),(33,1.5)],#3弦[(0,0.5),(44,3.5)],#4弦[(62,2),(62,2)]#6弦],[#第9节[(12,2),(12,2)],#1弦[(0,1.5),(24,0.5),(24,2)],#2弦[(0,1),(34,1),(34,2)],#3弦[(0,0.5),(44,1.5),(44,2)],#4弦[(51,4)]#5弦]]6弹奏吉他谱有了以上这些储备,写一段弹奏吉他谱的代码就是水到渠成了。全部代码不足行,重点位置已有注释。

importnumpyasnpimportwaveimportpyaudioSPEED=80#用每分钟节拍数表示弹奏速度FRAME_RATE=#采样速率(为CD音质,为调频广播音质)STEREO=True#立体声(双声道)#生成吉他音色包络线x=np.linspace(0,3*np.pi,2*int(FRAME_RATE*60/SPEED),endpoint=False)y1=1-x/(10*np.pi)+(1-x/(6*np.pi))*np.sin(x)*0.5x=np.arange(6*int(FRAME_RATE*60/SPEED))/int(FRAME_RATE*60/SPEED)y2=0.7*np.exp(-x)GUITAR_EFFECT_ARRAY=np.hstack((y1,y2))defget_frequency(pos):"""返回指定弦品pos的频率"""fs=(.6,.9,.0,.8,.0,82.4)ifpos[0]==0:return0else:returnfs[int(pos[0])-1]*pow(2,int(pos[1:])/12)defget_wave(f,beat):"""返回指定频率和节拍数的波形数据"""data=list()duration=beat*60/SPEEDsample_num=int(duration*FRAME_RATE)fork,pin[(1,0.4),(2,0.3),(3,0.2),(4,0.1)]:x=np.linspace(0,2*duration*f*k*np.pi,sample_num,endpoint=False)y=np.sin(x)*pdata.append(y)returnguitar_effect(np.sum(np.dstack(data)[0],axis=1))defguitar_effect(data):"""将等幅声波变成吉他音色的声波数据"""returndata*GUITAR_EFFECT_ARRAY[:data.shape[0]]defplay(melody,wave_file=None):"""弹奏吉他谱,若wave_file存在,同时生成.wav文件"""data=list()forsectioninmelody:data_section=list()forcordinsection:data_cord=list()forpos,beatincord:f=get_frequency(pos)dw=get_wave(f,beat)data_cord.append(dw)data_cord=np.hstack(data_cord)data_section.append(data_cord)d=data_section[0]foriinrange(1,len(data_section)):ifd.shape[0]data_section[i].shape[0]:d[:data_section[i].shape[0]]+=data_section[i]else:data_section[i][:d.shape[0]]+=dd=data_section[i]data.append(d)data=np.hstack(data)data=data*/data.max()data=data.astype(np.int16)ifSTEREO:blank=np.zeros(int(0.*FRAME_RATE),dtype=np.int16)d_left=np.hstack((data,blank))d_right=np.hstack((blank,data))data=np.dstack((d_left,d_right))[0].ravel()ifwave_file:withwave.open(wave_file,wb)asfp:fp.setparams((int(STEREO)+1,2,FRAME_RATE,0,NONE,NONE))fp.writeframes(data.tobytes())pa=pyaudio.PyAudio()stream=pa.open(format=pyaudio.paInt16,#设置量化精度:每个采样数据占用的位数channels=int(STEREO)+1,#设置通道数量rate=FRAME_RATE,#设置采样频率frames_per_buffer=,#设置声卡读写缓冲区output=True#设置声卡输出模式)foriinrange(0,data.shape[0],):stream.write(data[i:i+].tobytes())stream.stop_stream()stream.close()pa.terminate()用哪一首曲子来体验一下代码的效果呢?爱的罗曼史!这首曲子第二段的指法实在变态,对于手指僵硬的我来说,这辈子是不可能弹出第二段的。现在终于有机会弹出我所理解的这首世界名曲了。下面是根据约定翻译过来的吉他名曲“爱的罗曼史”的乐谱。

romance=[[[(17,1),(17,1),(17,1)],[(0,0.33),(20,1),(20,1),(20,0.66)],[(0,0.66),(30,1),(30,1),(30,0.33)],[(60,3)]],[[(17,1),(15,1),(13,1)],[(0,0.33),(20,1),(20,1),(20,0.66)],[(0,0.66),(30,1),(30,1),(30,0.33)],[(60,3)]],[[(13,1),(12,1),(10,1)],[(0,0.33),(20,1),(20,1),(20,0.66)],[(0,0.66),(30,1),(30,1),(30,0.33)],[(60,3)]],[[(10,1),(13,1),(17,1)],[(0,0.33),(20,1),(20,1),(20,0.66)],[(0,0.66),(30,1),(30,1),(30,0.33)],[(60,3)]],[[(,1),(,1),(,1)],[(0,0.33),(20,1),(20,1),(20,0.66)],[(0,0.66),(30,1),(30,1),(30,0.33)],[(60,3)]],[[(,1),(,1),(18,1)],[(0,0.33),(20,1),(20,1),(20,0.66)],[(0,0.66),(30,1),(30,1),(30,0.33)],[(60,3)]],[[(18,1),(17,1),(15,1)],[(0,0.33),(25,1),(25,1),(25,0.66)],[(0,0.66),(35,1),(35,1),(35,0.33)],[(50,3)]],[[(15,1),(17,1),(18,1)],[(0,0.33),(25,1),(25,1),(25,0.66)],[(0,0.66),(35,1),(35,1),(35,0.33)],[(50,3)]],[[(17,1),(18,1),(17,1)],[(0,0.33),(27,1),(27,1),(27,0.66)],[(0,0.66),(38,1),(38,1),(38,0.33)],[(67,3)]],[[(,1),(18,1),(17,1)],[(0,0.33),(27,1),(27,1),(27,0.66)],[(0,0.66),(38,1),(38,1),(38,0.33)],[(67,3)]],[[(17,1),(15,1),(13,1)],[(0,0.33),(20,1),(20,1),(20,0.66)],[(0,0.66),(30,1),(30,1),(30,0.33)],[(60,3)]],[[(13,1),(12,1),(10,1)],[(0,0.33),(20,1),(20,1),(20,0.66)],[(0,0.66),(30,1),(30,1),(30,0.33)],[(60,3)]],[[(12,1),(12,1),(12,1)],[(0,0.33),(20,1),(20,1),(20,0.66)],[(0,0.66),(32,1),(32,1),(32,0.33)],[(52,3)]],[[(12,1),(13,1),(12,1)],[(0,0.33),(20,1),(20,1),(20,0.66)],[(0,0.66),(32,1),(32,1),(32,0.33)],[(52,3)]],[[(10,1),(10,1),(10,1)],[(0,0.33),(20,1),(20,1),(20,0.66)],[(0,0.66),(30,1),(30,1),(30,0.33)],[(42,1),(52,1),(63,1)]],[[(10,3)],[(20,3)],[(30,3)],[(60,3)]],[[(14,1),(14,1),(14,1)],[(0,0.33),(20,1),(20,1),(20,0.66)],[(0,0.66),(31,1),(31,1),(31,0.33)],[(60,3)]],[[(14,1),(12,1),(10,1)],[(0,0.33),(20,1),(20,1),(20,0.66)],[(0,0.66),(31,1),(31,1),(31,0.33)],[(60,3)]],[[(25,1),(24,1),(24,1)],[(0,0.33),(32,1),(32,1),(32,0.66)],[(0,0.66),(44,1),(44,1),(44,0.33)],[(50,3)]],[[(24,1),(23,1),(24,1)],[(0,0.33),(32,1),(32,1),(32,0.66)],[(0,0.66),(44,1),(44,1),(44,0.33)],[(50,3)]],[[(19,1),(19,1),(19,1)],[(0,0.33),(27,1),(27,1),(27,0.66)],[(0,0.66),(38,1),(38,1),(38,0.33)],[(67,3)]],[[(19,1),(,1),(19,1)],[(0,0.33),(27,1),(27,1),(27,0.66)],[(0,0.66),(38,1),(38,1),(38,0.33)],[(67,3)]],[[(19,1),(17,1),(17,1)],[(0,0.33),(29,1),(29,1),(29,0.66)],[(0,0.66),(39,1),(39,1),(39,0.33)],[(60,3)]],[[(17,1),(19,1),(,1)],[(0,0.33),(29,1),(29,1),(29,0.66)],[(0,0.66),(39,1),(39,1),(39,0.33)],[(60,3)]],[[(,1),(,1),(,1)],[(0,0.33),(29,1),(29,1),(29,0.66)],[(0,0.66),(39,1),(39,1),(39,0.33)],[(60,3)]],[[(,1),(,1),(,1)],[(0,0.33),(29,1),(29,1),(29,0.66)],[(0,0.66),(39,1),(39,1),(39,0.33)],[(60,3)]],[[(19,1),(19,1),(19,1)],[(0,0.33),(25,1),(25,1),(25,0.66)],[(0,0.66),(36,1),(36,1),(36,0.33)],[(50,3)]],[[(19,1),(17,1),(15,1)],[(0,0.33),(25,1),(25,1),(25,0.66)],[(0,0.66),(36,1),(36,1),(36,0.33)],[(50,3)]],[[(14,1),(14,1),(14,1)],[(0,0.33),(24,1),(24,1),(24,0.66)],[(0,0.66),(32,1),(32,1),(32,0.33)],[(52,3)]],[[(14,1),(15,1),(12,1)],[(0,0.33),(24,1),(24,1),(24,0.66)],[(0,0.66),(32,1),(32,1),(32,0.33)],[(52,3)]],[[(10,1),(10,1),(10,1)],[(0,0.33),(20,1),(20,1),(20,0.66)],[(0,0.66),(31,1),(31,1),(31,0.33)],[(42,1),(52,1),(64,1)]],[[(10,3)],[(20,3)],[(30,3)],[(60,3)]]]激动人心的时刻来了,运行下面的代码,美妙的旋律就会从你的声卡中源源不断地飞出来。

play(romance)若想同时保存成文件,请使用下面的命令。

play(romance,爱的罗曼史.wav)大功告成!来听下最终效果:

还不错吧?

完整代码都在文中了,感兴趣的同学可以自己复制到代码中试一下,也可以改成你喜欢的曲目。

如果文章对你有帮助,欢迎转发/点赞/收藏~

作者:天元浪子

来源:Python作业辅导员

_往期文章推荐_

谁在用python弹奏一曲《菊花台》如需了解付费精品课程及教学答疑服务请在Crossin的编程教室内回复:预览时标签不可点收录于话题#个上一篇下一篇

转载请注明:http://www.lixiangguod.com/jhyt/22498.html
------分隔线----------------------------

热点文章

  • 没有热点文章

推荐文章

  • 没有推荐文章