Python对音频进行频谱分析

一、储备函数

1.1 计时装饰器、查找最接近的值、音高频率创建与读取

import time
import math
import numpy as np
import pandas as pd


# 定义时间装饰器
def timeit(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        using = (time.time() - start_time) * 1000
        print(f'运行时间:{using} 毫秒')
        return result
    return wrapper


# 普通未排序数组查找最接近的值
@timeit
def find_nearest(array, value):
    array = np.array(array)
    idx = (np.abs(array-value)).argmin()
    return array[idx]


# 已排序数组查找最接近的值
@timeit
def find_nearest_sorted(array, value):
    idx = np.searchsorted(array, value, side="left")
    if idx > 0 and (idx == len(array) or math.fabs(value - array[idx-1]) < math.fabs(value - array[idx])):
        return array[idx-1]
    else:
        return array[idx]


# 计算音高频率,参数一般为440、442或432
def calculate_frequency(standard_frequency):
    list_frequency = []
    for i in range(132):
        fre = round(standard_frequency / 32 * math.pow(2, (i-9.0) / 12),3)
        list_frequency.append(fre)
    return list_frequency[12:]


# 利用计算音高创建音高频率df
@timeit
def create_frequency():
    df_frequency = pd.DataFrame()
    df_frequency['letter_name'] = ['C0', 'C♯/D♭0', 'D0', 'D♯/E♭0', 'E0', 'F0', 'F♯/G♭0', 'G0', 'G♯/A♭0', 'A0', 'A♯/B♭0', 'B0', 'C1', 'C♯/D♭1', 'D1', 'D♯/E♭1', 'E1', 'F1', 'F♯/G♭1', 'G1', 'G♯/A♭1', 'A1', 'A♯/B♭1', 'B1', 'C2', 'C♯/D♭2', 'D2', 'D♯/E♭2', 'E2', 'F2', 'F♯/G♭2', 'G2', 'G♯/A♭2', 'A2', 'A♯/B♭2', 'B2', 'C3', 'C♯/D♭3', 'D3', 'D♯/E♭3', 'E3', 'F3', 'F♯/G♭3', 'G3', 'G♯/A♭3', 'A3', 'A♯/B♭3', 'B3', 'C4', 'C♯/D♭4', 'D4', 'D♯/E♭4', 'E4', 'F4', 'F♯/G♭4', 'G4', 'G♯/A♭4', 'A4', 'A♯/B♭4', 'B4', 'C5', 'C♯/D♭5', 'D5', 'D♯/E♭5', 'E5', 'F5', 'F♯/G♭5', 'G5', 'G♯/A♭5', 'A5', 'A♯/B♭5', 'B5', 'C6', 'C♯/D♭6', 'D6', 'D♯/E♭6', 'E6', 'F6', 'F♯/G♭6', 'G6', 'G♯/A♭6', 'A6', 'A♯/B♭6', 'B6', 'C7', 'C♯/D♭7', 'D7', 'D♯/E♭7', 'E7', 'F7', 'F♯/G♭7', 'G7', 'G♯/A♭7', 'A7', 'A♯/B♭7', 'B7', 'C8', 'C♯/D♭8', 'D8', 'D♯/E♭8', 'E8', 'F8', 'F♯/G♭8', 'G8', 'G♯/A♭8', 'A8', 'A♯/B♭8', 'B8', 'C9', 'C♯/D♭9', 'D9', 'D♯/E♭9', 'E9', 'F9', 'F♯/G♭9', 'G9', 'G♯/A♭9', 'A9', 'A♯/B♭9', 'B9']
    df_frequency['frequency_440'] = calculate_frequency(440)
    df_frequency['frequency_442'] = calculate_frequency(442)
    return df_frequency


# 利用读取音高创建音高频率df
@timeit
def read_frequency():
    df = pd.read_excel('f:/音高频率.xlsx', index_col='音名')
    letter_name = []
    for i in range(10):
        for j in df.index.to_list():
            letter_name.append(j.strip() + str(i))
    df_frequency = pd.DataFrame()
    df_frequency['frequency_440'] = df.stack().sort_values()
    df_frequency['letter_name'] = letter_name
    df_frequency.reset_index(inplace=True)
    df_frequency = df_frequency[['letter_name', 'frequency_440']]
    return df_frequency


df_frequency = create_frequency()   # 2.986431121826172 毫秒
# df_frequency = read_frequency()     # 378.781795501709 毫秒

df_frequency.to_excel('f:/frequency.xlsx')
print(df_frequency.loc[df_frequency['letter_name']=='A4'])

1.2 440音高频率表

在这里插入图片描述

音名0123456789
C16.35232.70365.406130.81261.63523.251046.5209341868372
C♯/D♭17.32434.64869.296138.59277.18554.371108.72217.54434.98869.8
D18.35436.70873.416146.83293.66587.331174.72349.34698.69397.3
D♯/E♭19.44538.89177.782155.56311.13622.251244.5248949789956.1
E20.60241.20382.407164.81329.63659.261318.52637527410548
F21.82743.65487.307174.61349.23698.461396.92793.85587.711175
F♯/G♭23.12546.24992.499185369.99739.99148029605919.911840
G24.548.99997.999196392783.99156831366271.912544
G♯/A♭25.95751.913103.83207.65415.3830.611661.23322.46644.913290
A27.55511022044088017603520704014080
A♯/B♭29.13558.27116.54233.08466.16932.331864.73729.37458.614917
B30.86861.735123.47246.94493.88987.771975.53951.17902.115804

在这里插入图片描述

对采样率为44.1kHz的AAC音频进行解码时,一帧的解码时间须控制在23.22毫秒内。

二、Pyaudio

我们可以使用Python自带的标准库wave。
但实际我并不推荐使用wave,用soundfile, librosa和pydub之类的第三方库会方便和强大得多

初衷 语音识别领域对音频文件进行频谱分析是一项基本的数据处理过程,同时也为后续的特征分析准备数据。

前驱知识
Python需要使用的相关库

wave
https://docs.python.org/3/library/wave.html
pyaudio
http://people.csail.mit.edu/hubert/pyaudio/
numpy
https://www.runoob.com/numpy/numpy-tutorial.html
pylab
https://www.programcreek.com/python/example/2345/pylab.title
音频帧概率详解
1.采样率(Sample Rate):每秒从连续信号中提取并组成离散信号的采样个数,它用赫兹(Hz)来表示。一般音乐CD的采样率是44100Hz,所以视频编码中的音频采样率保持在这个级别就完全足够了,通常视频转换器也将这个采样率作为默认设置。
2.帧率(Frame rate):是用于测量显示帧数的量度。所谓的测量单位为每秒显示帧数(Frames per Second,简称:FPS)或“赫兹”(Hz)。
3.码率(Bit Rate):指视频或音频文件在单位时间内使用的数据流量,该参数的单位通常是Kbps,也就是千比特每秒。通常2000kbps~3000kbps就已经足以将画质效果表现到极致了。码率参数与视频文件最终体积大小有直接性的关系
4.正常人听觉的频率范围大约在20Hz~20kHz之间,根据奈奎斯特采样理论,为了保证声音不失真,采样频率应该在40kHz左右。常用的音频采样频率有8kHz、11.025kHz、22.05kHz、16kHz、37.8kHz、44.1kHz、48kHz等,如果采用更高的采样频率,还可以达到DVD的音质

背景知识:
(一个AAC原始帧包含一段时间内1024个采样及相关数据)
分析:
1.AAC
音频帧的播放时间=一个AAC帧对应的采样样本的个数/采样频率(单位为s)
一帧 1024个 sample。采样率 Samplerate 44.1KHz,每秒44100个sample, 所以根据公式 音频帧的播放时间=一个AAC帧对应的采样样本的个数/采样频率
当前AAC一帧的播放时间是= 1024*1000/44100= 22.32ms(单位为ms)
2.MP3
mp3 每帧均为1152个字节, 则:
frame_duration = 1152 * 1000 / sample_rate
例如:sample_rate = 44100HZ时,计算出的时长为26.122ms,这就是经常听到的mp3每帧播放时间固定为26ms的由来。
3.H264
视频的播放时间跟帧率有关:
frame_duration = 1000/帧率(fps)
例如:fps = 25.00 ,计算出来的时常为40ms,这就是同行所说的40ms一帧视频数据。

# 打开提前准备的WAV文档,文件路径根据需要做修改

wf = wave.open("文档路径", "rb")

# 创建PyAudio对象
p = pyaudio.PyAudio()
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True
nframes = wf.getnframes()
framerate = wf.getframerate()


# 读取完整的帧数据到str_data中,这是一个string类型的数据
str_data = wf.readframes(nframes)
wf.close()


# 将音频波形数据转换为数组
# A new1-D array initialized from raw binary ortext data in a string.
wave_data = numpy.fromstring(str_data, dtype=numpy.short)


# 将wave_data数组改为2列,行数自动匹配。在修改shape的属性时,需使得数组的总长度不变。
wave_data.shape = -1,2


# 将数组转置
wave_data = wave_data.T
#time 也是一个数组,与wave_data[0]或wave_data[1]配对形成系列点坐标
#time = numpy.arange(0,nframes)*(1.0/framerate)


# 绘制波形图
#pylab.plot(time, wave_data[0])
#pylab.subplot(212)
#pylab.plot(time, wave_data[1], c="g")
#pylab.xlabel("time (seconds)")
#pylab.show()

# 采样点数,修改采样点数和起始位置进行不同位置和长度的音频波形分析
N=44100
start=0 #开始采样位置
df = framerate/(N-1) # 分辨率
freq = [df*n for n in range(0,N)] #N个元素
wave_data2=wave_data[0][start:start+N]
c=numpy.fft.fft(wave_data2)*2/N


# 常规显示采样频率一半的频谱
d=int(len(c)/2)


# 仅显示频率在4000以下的频谱
while freq[d]>4000:
d=10
pylab.plot(freq[:d-1],abs(c[:d-1]),'r')
pylab.show()
  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

岳涛@心馨电脑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值