python 语音学习 librosa语音特征Feature extraction

文档

在这里插入图片描述
这东西真好,提取特征是件挺麻烦的事情,预加重、分帧、加窗 … 不得不感叹py是真舒服。
相比python_speech_features的文档,librosa似乎更舒适

代码 “你好”的频谱

import matplotlib.pyplot as plt
import librosa.display
from python_speech_features import *
import numpy as np
import utils


x , sr = librosa.load("output.wav")
print(f'x.shape = {x.shape}')
print(f'sr = {sr}')
x=utils.trim_long_silences(x)


# -----------------------波形图------------------------------
librosa.display.waveplot(x)
plt.show()
# -----------------------短时傅里叶------------------------------
plt.title('librosa.stft')
# n_fft 为 FFT窗口大小 值越大,信号越清晰
stft = librosa.stft(x, n_fft=9216)
print("短时傅里叶")
print(stft.shape)
librosa.display.specshow(stft, x_axis='time', y_axis='hz')
plt.show()

# ------------------------梅尔-----------------------------
plt.title('librosa.feature.mfcc')
mfcc = librosa.feature.mfcc(x, n_fft=9216, n_mfcc = 20)
print("梅尔")
print(mfcc.shape)
librosa.display.specshow(mfcc, x_axis='time', y_axis='hz')
plt.show()
# ------------------------梅尔对数----------------------------
plt.title('librosa.feature.melspectrogram')
# hop_length 连续帧之间的样本数
melspec = librosa.feature.melspectrogram(x, 16000, n_fft=9216, hop_length=512)
logmelspec = librosa.power_to_db(melspec)
print("梅尔对数")
print(logmelspec.shape)
librosa.display.specshow(logmelspec, x_axis='time', y_axis='hz')
plt.show()
# ------------------------log Mel 能量----------------------------
plt.title('librosa.display.specshow')
logf=logfbank(x)
print("log Mel 能量")
print(logf.shape)
librosa.display.specshow(logf, x_axis='time', y_axis='hz')
plt.show()

# ------------------------光谱对比----------------------------
plt.title('librosa.feature.spectral_contrast')
stft = np.abs(librosa.stft(x,n_fft=9216))
contrast=librosa.feature.spectral_contrast(S=stft, sr=16000)
print("光谱对比")
print(contrast.shape)
librosa.display.specshow(contrast, x_axis='time', y_axis='hz')
plt.show()

# ------------------根据波形或功率谱图计算色谱图-------------------
plt.title('librosa.feature.chroma_stft')
chroma_stft=librosa.feature.chroma_stft(x,sr=16000)
print("色谱图")
print(chroma_stft.shape)
librosa.display.specshow(chroma_stft, x_axis='time', y_axis='hz')
plt.show()
# ------------------恒定Q色谱图-------------------
plt.title('librosa.feature.chroma_cqt')
chroma_cqt=librosa.feature.chroma_cqt(x,sr=16000)
librosa.display.specshow(chroma_cqt, x_axis='time', y_axis='hz')
print("恒定Q色谱图")
print(chroma_cqt.shape)
plt.show()
# ------------------色度能量归一化-------------------
plt.title('librosa.feature.chroma_cens')
chroma_cens=librosa.feature.chroma_cens(x,sr=16000)
librosa.display.specshow(chroma_cqt, x_axis='time', y_axis='hz')
print("色度能量归一化")
print(chroma_cens.shape)
plt.show()

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

import numpy as np
import librosa
import struct
import webrtcvad



from scipy.ndimage.morphology import binary_dilation

# ** a的b次方  32767
int16_max = (2 ** 15) - 1
audio_norm_target_dBFS=-30
partials_n_frames=160
sampling_rate=16000
mel_window_step = 10    # In milliseconds
mel_window_length = 25  # In milliseconds
mel_n_channels = 40

def trim_long_silences(wav,vad_window_length=30,sampling_rate=16000):

    # 计算语音检测窗口大小  //为整除 30秒X16000=总帧长
    samples_per_window = (vad_window_length * sampling_rate) // 1000

    # 修剪音频的结尾,使其具有窗口大小的倍数。使wav的长度能被 samples_per_window整除
    wav = wav[:len(wav) - (len(wav) % samples_per_window)]

    # 浮点数波形转换为16位单声道PCM  *:接收到的参数会形成一个元组,**:接收到的参数会形成一个字典。如下代码。
    # webrtcvad 的 is_speech 接收的是buf 所以这里需要转换
    pcm_wave = struct.pack("%dh" % len(wav), *(np.round(wav * int16_max)).astype(np.int16))

    # 执行语音激活检测
    voice_flags = []
    #  这里共有三种帧长可以用到,分别是80/10ms,160/20ms,240/30ms。其它采样率
    # 的48k,32k,24k,16k会重采样到8k来计算VAD。之所以选择上述三种帧长度,是因为语
    # 音信号是短时平稳信号,其在10ms~30ms之间可看成平稳信号,高斯马尔科夫等比较
    # 的信号处理方法基于的前提是信号是平稳的,在10ms~30ms,平稳信号处理方法是可
    # 以使用的。
    #   从vad的代码中可以看出,实际上,系统只处理默认10ms,20ms,30ms长度的数据,
    # 其它长度的数据没有支持,笔者修改过可以支持其它在10ms-30ms之间长度的帧长度
    # 发现也是可以的。
    #   vad检测共四种模式,用数字0~3来区分,激进程度与数值大小正相关。
    # 0: Normal,1:low Bitrate, 2:Aggressive;3:Very Aggressive 可以根据实际的使用
    vad = webrtcvad.Vad(mode=3)
    for window_start in range(0, len(wav), samples_per_window):
        window_end = window_start + samples_per_window
        # append 进来的都是Boolean  这里以samples_per_windowx2 的长度去检测是否为人声
        voice_flags.append(vad.is_speech(pcm_wave[window_start * 2:window_end * 2],
                                         sample_rate=sampling_rate))
    voice_flags = np.array(voice_flags)
    # 𝑣_𝑏𝑖𝑎𝑠𝑒𝑑𝑡=𝑣𝑡/(1−𝛽𝑡)
    # 滑动平均计算
    def moving_average(array, width):
        # 拼接 bool 二值化
        # width 执行滑动平均平滑时,帧的平均数。
        # 该值越大,VAD变化必须越大才能平滑。
        array_padded = np.concatenate((np.zeros((width - 1) // 2), array, np.zeros(width // 2)))
        # 一维数组累加
        ret = np.cumsum(array_padded, dtype=float)
        ret[width:] = ret[width:] - ret[:-width]
        return ret[width - 1:] / width

    #滑动平均计算
    audio_mask = moving_average(voice_flags, 8)
    #将平均数四舍五入 转bool
    audio_mask = np.round(audio_mask).astype(np.bool)

    # 扩张浊音区 使用多维二元膨胀 是数学形态学的方法 类似opencv 也有开闭运算 腐蚀膨胀
    # 举个栗子
    # >>> struct1 = ndimage.generate_binary_structure(2, 1)
    #     >>> struct1
    #     array([[False,  True, False],
    #            [ True,  True,  True],
    #            [False,  True, False]], dtype=bool)
    #     >>> # 3x3 structuring element with connectivity 2
    #     >>> struct2 = ndimage.generate_binary_structure(2, 2)
    #     >>> struct2
    #     array([[ True,  True,  True],
    #            [ True,  True,  True],
    #            [ True,  True,  True]], dtype=bool)
    #     >>> ndimage.binary_dilation(a, structure=struct1).astype(a.dtype)
    #     array([[ 0.,  0.,  0.,  0.,  0.],
    #            [ 0.,  0.,  1.,  0.,  0.],
    #            [ 0.,  1.,  1.,  1.,  0.],
    #            [ 0.,  0.,  1.,  0.,  0.],
    #            [ 0.,  0.,  0.,  0.,  0.]])
    #     >>> ndimage.binary_dilation(a, structure=struct2).astype(a.dtype)
    #     array([[ 0.,  0.,  0.,  0.,  0.],
    #            [ 0.,  1.,  1.,  1.,  0.],
    #            [ 0.,  1.,  1.,  1.,  0.],
    #            [ 0.,  1.,  1.,  1.,  0.],
    #            [ 0.,  0.,  0.,  0.,  0.]])
    audio_mask = binary_dilation(audio_mask, np.ones(6 + 1))
    #使其与wav一样大小
    audio_mask = np.repeat(audio_mask, samples_per_window)
    #通过这个遮罩扣掉没有声音那部分
    res=wav[audio_mask == True]
    return res

def normalize_volume(wav, target_dBFS, increase_only=False, decrease_only=False):
    if increase_only and decrease_only:
        raise ValueError("Both increase only and decrease only are set")
    rms = np.sqrt(np.mean((wav * int16_max) ** 2))
    wave_dBFS = 20 * np.log10(rms / int16_max)
    dBFS_change = target_dBFS - wave_dBFS
    if dBFS_change < 0 and increase_only or dBFS_change > 0 and decrease_only:
        return wav
    return wav * (10 ** (dBFS_change / 20))


def compute_partial_slices(n_samples, partial_utterance_n_frames=partials_n_frames,
                           min_pad_coverage=0.75, overlap=0.5):

    assert 0 <= overlap < 1
    assert 0 < min_pad_coverage <= 1
    # print(n_samples)

    # (16000*10毫秒)/1000
    samples_per_frame = int((sampling_rate * mel_window_step / 1000))
    # np.ceil 向正无穷取整 朝正无穷大方向取整
    n_frames = int(np.ceil((n_samples + 1) / samples_per_frame))
    # partial_utterance_n_frames 为语音中mel的帧数 160默认
    # overlap 若为0 则语音不连贯 这里默认0.5
    frame_step = max(int(np.round(partial_utterance_n_frames * (1 - overlap))), 1)

    # Compute the slices
    wav_slices, mel_slices = [], []
    steps = max(1, n_frames - partial_utterance_n_frames + frame_step + 1)
    # print(n_frames)  # 91
    # print(frame_step)  # 80
    # print(steps)  # 12
    # print(samples_per_frame)

    for i in range(0, steps, frame_step):
        mel_range = np.array([i, i + partial_utterance_n_frames])
        wav_range = mel_range * samples_per_frame
        mel_slices.append(slice(*mel_range))
        wav_slices.append(slice(*wav_range))

    # Evaluate whether extra padding is warranted or not
    # 评估是否需要额外填充
    last_wav_range = wav_slices[-1]
    coverage = (n_samples - last_wav_range.start) / (last_wav_range.stop - last_wav_range.start)
    if coverage < min_pad_coverage and len(mel_slices) > 1:
        mel_slices = mel_slices[:-1]
        wav_slices = wav_slices[:-1]

    return wav_slices, mel_slices

def wav_to_mel_spectrogram(wav):
    """
    从预处理的音频波形导出可供编码器使用的mel谱图。
    注:这不是对数mel谱图。
    """
    frames = librosa.feature.melspectrogram(
        wav,
        sampling_rate,
        n_fft=int(sampling_rate * mel_window_length / 1000),
        hop_length=int(sampling_rate * mel_window_step / 1000),
        n_mels=mel_n_channels
    )
    return frames.astype(np.float32).T

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值