用NumPy“弹奏”一首CD级音质的《爱的罗曼史》
Crossin的编程教室
共 30224字,需浏览 61分钟
·
2021-05-11 07:29
1 前言
pip install PyAudio
2 发出单一频率的声音
>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> x = np.linspace(0, 1158*np.pi, 66150, endpoint=False)
>>> y = np.sin(x) * 32767
>>> y.dtype
dtype('float64')
>>> y = y.astype(np.int16)
>>> y.dtype
dtype('int16')
>>> plt.plot(x, y, color='green')
>>> plt.show()
>>> import wave
>>> with wave.open(r'd:\sound_196Hz_3s.wav', 'wb') as fp:
fp.setparams((1, 2, 22050, 0, 'NONE', 'NONE'))
fp.writeframes(y.tobytes())
3 模拟吉他音色
import pyaudio
import numpy as np
import time
def capture(rate, chunk):
pa = pyaudio.PyAudio()
stream = pa.open(
format = pyaudio.paInt16, # 设置量化精度(每个采样数据占用的位数)
channels = 1, # 设置单声道模式
rate = 22050, # 设置采样频率
frames_per_buffer = 2205, # 设置声卡读写缓冲区
input = True # 设置声卡输出模式
)
data = list()
while len(data) < 50:
data.append(np.frombuffer(stream.read(2205), dtype=np.int16))
stream.close()
pa.terminate()
return np.hstack(data)
if __name__ == '__main__':
for i in range(5):
print(5-i)
time.sleep(1)
print('Start')
data = capture(22050, 1024)
np.save('吉他10.npy', data)
import numpy as np
import matplotlib.pyplot as plt
import time
import wave
plt.rcParams['font.sans-serif'] = ['FangSong']
plt.rcParams['axes.unicode_minus'] = False
d1 = np.load('吉他10.npy')
d2 = np.load('吉他20.npy')
plt.subplot(221)
plt.plot(d1, c='g')
plt.title('吉他1弦空弦音')
plt.subplot(222)
plt.plot(d2, c='m')
plt.title('吉他2弦空弦音')
#plt.show()
fd1 = np.fft.fft(d1[22050:44100]) # 截取吉他1弦空弦音1秒钟的数据进行傅里叶分析
fd2 = np.fft.fft(d2[22050:44100]) # 截取吉他2弦空弦音1秒钟的数据进行傅里叶分析
plt.subplot(223)
plt.plot(np.abs(fd1[:11025]/22050), c='g')
plt.title('吉他1弦单边频谱图')
plt.subplot(224)
plt.plot(np.abs(fd2[:11025]/22050), c='m')
plt.title('吉他2弦单边频谱图')
plt.show()
声音幅度在振动中逐渐变小
频谱显示存在较强幅度的二倍频、三倍频、四倍频
>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> x = np.linspace(0, 3*np.pi, 22050, endpoint=False)
>>> y1 = 1-x/(10*np.pi)+(1-x/(6*np.pi))*np.sin(x)*0.5
>>> x = np.arange(3*22050)/22050
>>> y2 = 0.7*np.exp(-x)
>>> GUITAR_EFFECT_ARRAY = np.hstack((y1, y2))
>>> y_guitar = y*GUITAR_EFFECT_ARRAY[:y.shape[0]]
>>> plt.subplot(121)
>>> plt.plot(GUITAR_EFFECT_ARRAY)
>>> plt.subplot(122)
>>> plt.plot(y_guitar)
>>> plt.show()
>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> plt.rcParams['font.sans-serif']=['FangSong']
>>> plt.rcParams['axes.unicode_minus']=False
>>> x1 = np.linspace(0, 4*np.pi, 300)
>>> x2 = np.linspace(0, 8*np.pi, 300)
>>> x3 = np.linspace(0, 12*np.pi, 300)
>>> 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(221)
>>> plt.title('基波')
>>> plt.plot(y1, c='be')
>>> plt.subplot(222)
>>> plt.title('二倍频')
>>> plt.plot(y2, c='cn')
>>> plt.subplot(223)
>>> plt.title('三倍频')
>>> plt.plot(y3, c='y')
>>> plt.subplot(224)
>>> plt.title('复合波形')
>>> plt.plot(y, c='m')
>>> plt.show()
4 弹出吉他上所有的音符
def get_frequency(pos):
"""返回指定弦品pos的频率"""
fs = (329.6, 246.9, 196.0, 146.8, 110.0, 82.4)
if pos[0] == '0':
return 0
else:
return fs[int(pos[0])-1] * pow(2, int(pos[1:])/12)
5 吉他谱的格式约定
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 弹奏吉他谱
import numpy as np
import wave
import pyaudio
SPEED = 80 # 用每分钟节拍数表示弹奏速度
FRAME_RATE = 44100 # 采样速率(44100为CD音质,22050为调频广播音质)
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.5
x = 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))
def get_frequency(pos):
"""返回指定弦品pos的频率"""
fs = (329.6, 246.9, 196.0, 146.8, 110.0, 82.4)
if pos[0] == '0':
return 0
else:
return fs[int(pos[0])-1] * pow(2, int(pos[1:])/12)
def get_wave(f, beat):
"""返回指定频率和节拍数的波形数据"""
data = list()
duration = beat*60/SPEED
sample_num = int(duration*FRAME_RATE)
for k, p in [(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)*p
data.append(y)
return guitar_effect(np.sum(np.dstack(data)[0], axis=1))
def guitar_effect(data):
"""将等幅声波变成吉他音色的声波数据"""
return data*GUITAR_EFFECT_ARRAY[:data.shape[0]]
def play(melody, wave_file=None):
"""弹奏吉他谱,若wave_file存在,同时生成.wav文件"""
data = list()
for section in melody:
data_section = list()
for cord in section:
data_cord = list()
for pos, beat in cord:
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]
for i in range(1, len(data_section)):
if d.shape[0] > data_section[i].shape[0]:
d[:data_section[i].shape[0]] += data_section[i]
else:
data_section[i][:d.shape[0]] += d
d = data_section[i]
data.append(d)
data = np.hstack(data)
data = data*20000/data.max()
data = data.astype(np.int16)
if STEREO:
blank = np.zeros(int(0.006*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()
if wave_file:
with wave.open(wave_file, 'wb') as fp:
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 = 1024, # 设置声卡读写缓冲区
output = True # 设置声卡输出模式
)
for i in range(0, data.shape[0], 1024):
stream.write(data[i:i+1024].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)]
],
[
[('112',1),('112',1),('112',1)],
[('0',0.33),('20',1),('20',1),('20',0.66)],
[('0',0.66),('30',1),('30',1),('30',0.33)],
[('60',3)]
],
[
[('112',1),('110',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)]
],
[
[('111',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),('111',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),('111',1)],
[('0',0.33),('29',1),('29',1),('29',0.66)],
[('0',0.66),('39',1),('39',1),('39',0.33)],
[('60',3)]
],
[
[('112',1),('112',1),('112',1)],
[('0',0.33),('29',1),('29',1),('29',0.66)],
[('0',0.66),('39',1),('39',1),('39',0.33)],
[('60',3)]
],
[
[('112',1),('111',1),('110',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')
还不错吧?
完整代码都在文中了,感兴趣的同学可以自己复制到代码中试一下,也可以改成你喜欢的曲目。
如果文章对你有帮助,欢迎转发/点赞/收藏~
作者:天元浪子
_往期文章推荐_
评论