大家好,欢迎来到IT知识分享网。
梅尔频率倒谱系数(MFCC,Mel-Frequency Cepstral Coefficients)是一种用于处理和分析音频信号的技术。它特别适用于语音和音频处理领域。MFCC 的主要目的是将音频信号转换为一组特征值,这些特征值可以用来进行进一步的分析和处理,比如语音识别、说话人识别和音频分类等。
MFCC 的基本概念
MFCC 是通过模拟人耳对声音的感知方式来提取音频信号的特征。人耳对不同频率的声音有不同的敏感度,低频声音和高频声音的感知方式不同。MFCC 利用这一点,通过一系列步骤将音频信号转换为一组特征值,这些特征值能够有效地表示音频信号的特性。
MFCC 的应用
梅尔频率倒谱系数(MFCC)在音频和语音处理领域有广泛的应用。以下是一些主要的应用场景:
应用场景 |
说明 |
语音识别 |
MFCC 是语音识别系统中最常用的特征之一。它们能够有效地捕捉语音信号中的重要信息,使得机器能够识别和理解人类的语言。语音识别系统通常会将输入的语音信号转换为 MFCC 特征,然后使用这些特征进行模式匹配和分类。 |
说话人识别 |
在说话人识别系统中,MFCC 用于提取每个说话者的独特特征。通过分析不同说话者的 MFCC 特征,系统可以区分和识别不同的说话者。这在安全认证、语音命令控制等应用中非常有用。 |
音频分类 |
MFCC 也广泛应用于音频分类任务中,例如音乐流派分类、环境声音分类等。通过提取音频信号的 MFCC 特征,机器学习算法可以对不同类型的音频进行分类和识别。 |
语音情感识别 |
在语音情感识别中,MFCC 用于捕捉语音信号中的情感特征。通过分析说话者的语音特征,系统可以识别出说话者的情感状态,如愤怒、快乐、悲伤等。 |
语音增强 |
MFCC 还可以用于语音增强和降噪处理。通过分析和处理语音信号的 MFCC 特征,可以有效地去除背景噪音,增强语音的清晰度和可懂度。 |
语音合成 |
在语音合成系统中,MFCC 用于生成自然的语音信号。通过分析目标语音的 MFCC 特征,语音合成系统可以生成与目标语音相似的合成语音。 |
语言识别 |
MFCC 也用于自动语言识别系统中,通过分析语音信号的特征,系统可以识别出说话者使用的语言。 |
音乐信息检索 |
在音乐信息检索中,MFCC 用于提取音乐信号的特征,以便进行音乐相似性搜索、音乐推荐等任务。 |
语音转换 |
在语音转换系统中,MFCC 用于将一个说话者的语音转换为另一个说话者的语音。通过分析和转换 MFCC 特征,可以实现语音的风格转换。 |
语音质量评估 |
MFCC 还可以用于评估语音信号的质量,通过分析语音信号的特征,可以判断语音的清晰度、自然度等质量指标。 |
MFCC 的计算步骤
1. 预加重:放大高频部分。
2. 分帧:将信号分成短帧。
3. 加窗:对每个帧应用窗函数。
4. 快速傅里叶变换 (FFT):将每个帧从时域转换到频域。
5. 梅尔滤波器组:应用一组模拟人耳响应的滤波器。
6. 对数运算:对滤波器组能量取对数。
7. 离散余弦变换 (DCT):将对数滤波器组能量转换到倒谱域。
以下是对每个步骤的详细解释,并分别使用 .NET+C#+NWaves 和 python+librosa 演示实现:
对于 .NET 工程,你需要先给工程打上 NWaves包 : Install-Package NWaves
对于 Python工程,你需要先打上 librosa 和 scipy 两个轮子:pip install librosa
1. 预加重(Pre-emphasis)
预加重(Pre-emphasis)是 MFCC 计算过程中的一个重要步骤。它的目的是增强高频成分,补偿由于声道传输特性导致的高频衰减。预加重通常通过一个高通滤波器来实现。预加重的公式通常如下:
y(t)=x(t)−αx(t−1)
其中:
- y(t)y(t) 是预加重后的信号。
- x(t)x(t) 是原始输入信号。
- α\alpha 是预加重系数,通常取值为 0.95 或 0.97。
通过预加重,可以在后续的MFCC特征提取过程中更好地保留语音信号的细节。
预加重的实现步骤
1. 读取音频信号:从音频文件中读取原始信号。
2. 应用预加重滤波器:使用上述公式对信号进行预加重处理。
以下是一个使用 NWaves库在 C# 中实现预加重的示例:
using System; using System.IO; using NWaves.Audio; using NWaves.Signals; using NWaves.Filters.Base; class Program { static void Main(string[] args) { // 加载音频文件 var waveFile = new WaveFile(File.OpenRead("your_audio_file.wav")); var signal = waveFile[Channels.Left]; // 预加重系数 double preEmphasisAlpha = 0.97; // 创建预加重滤波器 var preEmphasisFilter = new PreEmphasisFilter(preEmphasisAlpha); // 应用预加重滤波器 var preEmphasizedSignal = preEmphasisFilter.ApplyTo(signal); // 打印预加重后的信号 foreach (var sample in preEmphasizedSignal.Samples) { Console.WriteLine(sample); } } } // 预加重滤波器类 public class PreEmphasisFilter : IFilter { private readonly double _alpha; public PreEmphasisFilter(double alpha) { _alpha = alpha; } public DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto) { var output = new float[signal.Length]; output[0] = signal[0]; for (int i = 1; i < signal.Length; i++) { output[i] = (float)(signal[i] - _alpha * signal[i - 1]); } return new DiscreteSignal(signal.SamplingRate, output); } }
下面是使用python的实现:
import numpy as np import librosa import matplotlib.pyplot as plt # 读取音频文件 y, sr = librosa.load('your_audio_file.wav', sr=None) # 预加重系数 pre_emphasis = 0.97 # 应用预加重滤波器 y_preemphasized = np.append(y[0], y[1:] - pre_emphasis * y[:-1]) # 打印预加重后的信号 print(y_preemphasized) # 可视化原始信号和预加重后的信号 plt.figure(figsize=(12, 6)) plt.subplot(2, 1, 1) plt.plot(y) plt.title('Original Signal') plt.subplot(2, 1, 2) plt.plot(y_preemphasized) plt.title('Pre-emphasized Signal') plt.tight_layout() plt.show()
2. 分帧(Framing)
在 MFCC(梅尔频率倒谱系数)计算过程中,分帧(Framing)是一个重要的步骤。分帧的目的是将连续的音频信号分割成多个短时帧,以便在每个帧上进行傅里叶变换和其他处理。这样可以捕捉到信号的短时特性。
分帧的步骤
1. 选择帧长度和帧移:通常选择 20-40 毫秒的帧长度(例如 25 毫秒),帧移(帧间隔)通常为 10 毫秒。
2. 分割信号:将音频信号分割成多个重叠的帧,每个帧包含一定数量的样本。
以下是一个使用 NWaves 库在 C# 中实现分帧的示例:
using System; using System.IO; using NWaves.Audio; using NWaves.Signals; using NWaves.Transforms; using NWaves.Windows; class Program { static void Main(string[] args) { // 加载音频文件 var waveFile = new WaveFile(File.OpenRead("your_audio_file.wav")); var signal = waveFile[Channels.Left]; // 设置帧长度和帧移(以样本数表示) int frameSize = (int)(0.025 * signal.SamplingRate); // 25 毫秒的帧 int hopSize = (int)(0.01 * signal.SamplingRate); // 10 毫秒的帧移 // 分帧 var frames = signal.Frames(frameSize, hopSize); // 打印分帧后的信号 Console.WriteLine(#34;Number of frames: {frames.Count}"); foreach (var frame in frames) { Console.WriteLine(string.Join(", ", frame)); } // 可视化第一个帧 var firstFrame = frames[0]; for (int i = 0; i < firstFrame.Length; i++) { Console.WriteLine(firstFrame[i]); } } }
在这个示例中:
• WaveFile 类用于加载音频文件。
• Frames 方法用于将音频信号分割成多个帧。
• frameSize 和 hopSize 分别表示帧长度和帧移,以样本数表示。
以下是一个使用 librosa 库在 Python 中实现分帧的示例:
import numpy as np import librosa # 读取音频文件 y, sr = librosa.load('your_audio_file.wav', sr=None) # 设置帧长度和帧移(以样本数表示) frame_length = int(0.025 * sr) # 25 毫秒的帧 hop_length = int(0.01 * sr) # 10 毫秒的帧移 # 使用 librosa 的 framesig 函数进行分帧 frames = librosa.util.frame(y, frame_length=frame_length, hop_length=hop_length).T # 打印分帧后的信号 print(frames.shape) # (num_frames, frame_length) print(frames) # 可视化第一个帧 import matplotlib.pyplot as plt plt.figure(figsize=(10, 4)) plt.plot(frames[0]) plt.title('First Frame') plt.show()
在这个示例中:
• librosa.load 用于加载音频文件。
• librosa.util.frame 用于将音频信号分割成多个帧。
• matplotlib.pyplot 用于可视化第一个帧。
3. 加窗(Windowing)
加窗的目的是减少信号在帧边界处的频谱泄漏(Spectral Leakage),从而提高频谱分析的准确性。通常使用的窗函数有汉明窗(Hamming Window)、汉宁窗(Hanning Window)等。
加窗的步骤
1. 选择窗函数:常用的窗函数包括哈明窗、汉宁窗、矩形窗等。
2. 应用窗函数:将选择的窗函数应用到每个帧上。
()=()⋅()
以下是一个使用 NWaves 库在 C# 中实现加窗的示例:
using System; using System.IO; using NWaves.Audio; using NWaves.Signals; using NWaves.Windows; class Program { static void Main(string[] args) { // 加载音频文件 var waveFile = new WaveFile(File.OpenRead("your_audio_file.wav")); var signal = waveFile[Channels.Left]; // 设置帧长度和帧移(以样本数表示) int frameSize = (int)(0.025 * signal.SamplingRate); // 25 毫秒的帧 int hopSize = (int)(0.01 * signal.SamplingRate); // 10 毫秒的帧移 // 分帧 var frames = signal.Frames(frameSize, hopSize); // 选择窗函数(例如汉明窗) var window = Window.OfType(WindowTypes.Hamming, frameSize); // 对每个帧应用窗函数 foreach (var frame in frames) { for (int i = 0; i < frame.Length; i++) { frame[i] *= window[i]; } } // 打印加窗后的信号 Console.WriteLine(#34;Number of frames: {frames.Count}"); foreach (var frame in frames) { Console.WriteLine(string.Join(", ", frame)); } // 可视化第一个加窗后的帧 var firstFrame = frames[0]; for (int i = 0; i < firstFrame.Length; i++) { Console.WriteLine(firstFrame[i]); } } }
在这个示例中:
• WaveFile 类用于加载音频文件。
• Frames 方法用于将音频信号分割成多个帧。
• Window.OfType 方法用于生成指定类型的窗函数(例如汉明窗)。
• 对每个帧应用窗函数。
以下是一个使用 librosa 库在 Python 中实现加窗的示例:
import numpy as np import librosa import matplotlib.pyplot as plt # 读取音频文件 y, sr = librosa.load('your_audio_file.wav', sr=None) # 设置帧长度和帧移(以样本数表示) frame_length = int(0.025 * sr) # 25 毫秒的帧 hop_length = int(0.01 * sr) # 10 毫秒的帧移 # 分帧 frames = librosa.util.frame(y, frame_length=frame_length, hop_length=hop_length).T # 选择窗函数(例如汉明窗) window = np.hamming(frame_length) # 对每个帧应用窗函数 windowed_frames = frames * window # 打印加窗后的信号 print(windowed_frames.shape) # (num_frames, frame_length) print(windowed_frames) # 可视化第一个加窗后的帧 plt.figure(figsize=(10, 4)) plt.plot(windowed_frames[0]) plt.title('First Windowed Frame') plt.show()
在这个示例中:
• librosa.load 用于加载音频文件。
• librosa.util.frame 用于将音频信号分割成多个帧。
• np.hamming 用于生成哈明窗。
• 对每个帧应用窗函数。
4.快速傅里叶变换 (FFT)
FFT 的目的是将时间域信号转换为频域信号,以便进行频谱分析。以下是详细的解释和实现步骤:
FFT 的步骤
1. 分帧和加窗:将音频信号分割成多个短时帧,并对每个帧应用窗函数。
2. 计算每个帧的 FFT:对每个加窗后的帧进行快速傅里叶变换,得到频谱。
3. 计算功率谱:计算每个频率分量的功率谱。
以下是一个使用 NWaves 库在 C# 中实现 FFT 的示例:
using System; using System.IO; using NWaves.Audio; using NWaves.Signals; using NWaves.Transforms; using NWaves.Windows; class Program { static void Main(string[] args) { // 加载音频文件 var waveFile = new WaveFile(File.OpenRead("your_audio_file.wav")); var signal = waveFile[Channels.Left]; // 设置帧长度和帧移(以样本数表示) int frameSize = (int)(0.025 * signal.SamplingRate); // 25 毫秒的帧 int hopSize = (int)(0.01 * signal.SamplingRate); // 10 毫秒的帧移 // 分帧 var frames = signal.Frames(frameSize, hopSize); // 选择窗函数(例如汉明窗) var window = Window.OfType(WindowTypes.Hamming, frameSize); // 创建 FFT 变换器 var fft = new Fft(frameSize); // 对每个帧应用窗函数并计算 FFT foreach (var frame in frames) { // 应用窗函数 for (int i = 0; i < frame.Length; i++) { frame[i] *= window[i]; } // 计算 FFT var spectrum = new float[frameSize]; fft.Magnitude(frame, spectrum); // 打印功率谱 for (int i = 0; i < spectrum.Length; i++) { Console.WriteLine(spectrum[i]); } } } }
在这个示例中:
• WaveFile 类用于加载音频文件。
• Frames 方法用于将音频信号分割成多个帧。
• Window.OfType 方法用于生成指定类型的窗函数(例如汉明窗)。
• Fft 类用于创建 FFT 变换器。
• Magnitude 方法用于计算每个帧的 FFT 幅度谱。
以下是一个使用 librosa 库在 Python 中实现 FFT 的示例:
import numpy as np import librosa import matplotlib.pyplot as plt # 读取音频文件 y, sr = librosa.load('your_audio_file.wav', sr=None) # 设置帧长度和帧移(以样本数表示) frame_length = int(0.025 * sr) # 25 毫秒的帧 hop_length = int(0.01 * sr) # 10 毫秒的帧移 # 分帧 frames = librosa.util.frame(y, frame_length=frame_length, hop_length=hop_length).T # 选择窗函数(例如汉明窗) window = np.hamming(frame_length) # 对每个帧应用窗函数 windowed_frames = frames * window # 计算每个帧的 FFT fft_frames = np.fft.rfft(windowed_frames, axis=1) # 计算功率谱 power_spectrum = np.abs(fft_frames) 2 # 打印功率谱 print(power_spectrum.shape) # (num_frames, frame_length // 2 + 1) print(power_spectrum) # 可视化第一个帧的功率谱 plt.figure(figsize=(10, 4)) plt.plot(power_spectrum[0]) plt.title('Power Spectrum of First Frame') plt.show()
这个示例中:
• librosa.load 用于加载音频文件。
• librosa.util.frame 用于将音频信号分割成多个帧。
• np.hamming 用于生成汉明窗。
• np.fft.rfft 用于计算每个帧的快速傅里叶变换。
• np.abs 和平方操作用于计算功率谱。
5. 梅尔滤波器组
梅尔滤波器组的目的是将频谱转换到梅尔频率尺度上,以更好地模拟人耳对不同频率的感知。以下是详细的解释和实现步骤:
1. 定义梅尔频率尺度:梅尔频率尺度是一种非线性频率尺度,更符合人耳对频率的感知。梅尔频率 ( m ) 和线性频率 ( f ) 之间的关系可以通过以下公式转换:

2. 创建滤波器组:在梅尔频率尺度上等间距地放置滤波器,并将这些滤波器映射回线性频率尺度。每个滤波器通常是三角形的,覆盖一定范围的频率。
3. 应用滤波器组:对每个帧的频谱应用梅尔滤波器组,计算每个滤波器的能量。
以下是一个使用 NWaves 库在 C# 中实现梅尔滤波器组的示例:
using System; using System.IO; using NWaves.Audio; using NWaves.FeatureExtractors; using NWaves.FeatureExtractors.Options; using NWaves.Transforms; using NWaves.Windows; class Program { static void Main(string[] args) { // 加载音频文件 var waveFile = new WaveFile(File.OpenRead("your_audio_file.wav")); var signal = waveFile[Channels.Left]; // 设置帧长度和帧移(以样本数表示) int frameSize = (int)(0.025 * signal.SamplingRate); // 25 毫秒的帧 int hopSize = (int)(0.01 * signal.SamplingRate); // 10 毫秒的帧移 // 分帧 var frames = signal.Frames(frameSize, hopSize); // 选择窗函数(例如汉明窗) var window = Window.OfType(WindowTypes.Hamming, frameSize); // 创建 FFT 变换器 var fft = new Fft(frameSize); // 创建梅尔滤波器组 var melFilterBank = FilterBanks.MelBank(40, frameSize, signal.SamplingRate, 0, signal.SamplingRate / 2); // 对每个帧应用窗函数并计算 FFT foreach (var frame in frames) { // 应用窗函数 for (int i = 0; i < frame.Length; i++) { frame[i] *= window[i]; } // 计算 FFT var spectrum = new float[frameSize]; fft.Magnitude(frame, spectrum); // 计算梅尔滤波器组能量 var melEnergies = new float[melFilterBank.Length]; for (int i = 0; i < melFilterBank.Length; i++) { melEnergies[i] = 0; for (int j = 0; j < melFilterBank[i].Length; j++) { melEnergies[i] += spectrum[j] * melFilterBank[i][j]; } } // 打印梅尔滤波器组能量 Console.WriteLine(string.Join(", ", melEnergies)); } } }
在这个示例中:
• WaveFile 类用于加载音频文件。
• Frames 方法用于将音频信号分割成多个帧。
• Window.OfType 方法用于生成指定类型的窗函数(例如汉明窗)。
• Fft 类用于创建 FFT 变换器。
• FilterBanks.MelBank 方法用于创建梅尔滤波器组。
• 对每个帧的频谱应用梅尔滤波器组,计算能量。
以下是一个使用 librosa 库在 Python 中实现梅尔滤波器组的示例:
import numpy as np import librosa import matplotlib.pyplot as plt # 读取音频文件 y, sr = librosa.load('your_audio_file.wav', sr=None) # 设置帧长度和帧移(以样本数表示) frame_length = int(0.025 * sr) # 25 毫秒的帧 hop_length = int(0.01 * sr) # 10 毫秒的帧移 # 分帧 frames = librosa.util.frame(y, frame_length=frame_length, hop_length=hop_length).T # 选择窗函数(例如汉明窗) window = np.hamming(frame_length) # 对每个帧应用窗函数 windowed_frames = frames * window # 计算每个帧的 FFT fft_frames = np.fft.rfft(windowed_frames, axis=1) # 计算功率谱 power_spectrum = np.abs(fft_frames) 2 # 创建梅尔滤波器组 n_mels = 40 mel_filter_bank = librosa.filters.mel(sr=sr, n_fft=frame_length, n_mels=n_mels) # 应用梅尔滤波器组 mel_energies = np.dot(power_spectrum, mel_filter_bank.T) # 打印梅尔滤波器组能量 print(mel_energies.shape) # (num_frames, n_mels) print(mel_energies) # 可视化第一个帧的梅尔滤波器组能量 plt.figure(figsize=(10, 4)) plt.plot(mel_energies[0]) plt.title('Mel Filter Bank Energies of First Frame') plt.show()
在这个示例中:
• librosa.load 用于加载音频文件。
• librosa.util.frame 用于将音频信号分割成多个帧。
• np.hamming 用于生成汉明窗。
• np.fft.rfft 用于计算每个帧的快速傅里叶变换。
• np.abs 和平方操作用于计算功率谱。
• librosa.filters.mel 用于创建梅尔滤波器组。
• np.dot 用于应用梅尔滤波器组,计算每个滤波器的能量。
6. 对数运算
这一过程的目的是将频谱的幅度转换为对数尺度,以更好地模拟人耳对声音强度的感知。具体来说,对数运算是在计算梅尔滤波器组能量之后进行的。
对数运算的步骤
1. 计算梅尔滤波器组能量:对每个帧的频谱应用梅尔滤波器组,得到每个滤波器的能量。
2. 对能量取对数:对每个滤波器的能量取对数,得到对数能量。
以下是一个使用 NWaves 库在 C# 中实现对数运算的示例:
using System; using System.IO; using NWaves.Audio; using NWaves.FeatureExtractors; using NWaves.FeatureExtractors.Options; using NWaves.Transforms; using NWaves.Windows; class Program { static void Main(string[] args) { // 加载音频文件 var waveFile = new WaveFile(File.OpenRead("your_audio_file.wav")); var signal = waveFile[Channels.Left]; // 设置帧长度和帧移(以样本数表示) int frameSize = (int)(0.025 * signal.SamplingRate); // 25 毫秒的帧 int hopSize = (int)(0.01 * signal.SamplingRate); // 10 毫秒的帧移 // 分帧 var frames = signal.Frames(frameSize, hopSize); // 选择窗函数(例如汉明窗) var window = Window.OfType(WindowTypes.Hamming, frameSize); // 创建 FFT 变换器 var fft = new Fft(frameSize); // 创建梅尔滤波器组 var melFilterBank = FilterBanks.MelBank(40, frameSize, signal.SamplingRate, 0, signal.SamplingRate / 2); // 对每个帧应用窗函数并计算 FFT foreach (var frame in frames) { // 应用窗函数 for (int i = 0; i < frame.Length; i++) { frame[i] *= window[i]; } // 计算 FFT var spectrum = new float[frameSize]; fft.Magnitude(frame, spectrum); // 计算梅尔滤波器组能量 var melEnergies = new float[melFilterBank.Length]; for (int i = 0; i < melFilterBank.Length; i++) { melEnergies[i] = 0; for (int j = 0; j < melFilterBank[i].Length; j++) { melEnergies[i] += spectrum[j] * melFilterBank[i][j]; } } // 对能量取对数 for (int i = 0; i < melEnergies.Length; i++) { melEnergies[i] = (float)Math.Log(melEnergies[i] + 1e-10); // 加上一个小值以避免对数零 } // 打印对数能量 Console.WriteLine(string.Join(", ", melEnergies)); } } }
在这个示例中:
• WaveFile 类用于加载音频文件。
• Frames 方法用于将音频信号分割成多个帧。
• Window.OfType 方法用于生成指定类型的窗函数(例如汉明窗)。
• Fft 类用于创建 FFT 变换器。
• FilterBanks.MelBank 方法用于创建梅尔滤波器组。
• 对每个帧的频谱应用梅尔滤波器组,计算能量。
• 对每个滤波器的能量取对数。
以下是一个使用 librosa 库在 Python 中实现对数运算的示例:
import numpy as np import librosa import matplotlib.pyplot as plt # 读取音频文件 y, sr = librosa.load('your_audio_file.wav', sr=None) # 设置帧长度和帧移(以样本数表示) frame_length = int(0.025 * sr) # 25 毫秒的帧 hop_length = int(0.01 * sr) # 10 毫秒的帧移 # 分帧 frames = librosa.util.frame(y, frame_length=frame_length, hop_length=hop_length).T # 选择窗函数(例如汉明窗) window = np.hamming(frame_length) # 对每个帧应用窗函数 windowed_frames = frames * window # 计算每个帧的 FFT fft_frames = np.fft.rfft(windowed_frames, axis=1) # 计算功率谱 power_spectrum = np.abs(fft_frames) 2 # 计算梅尔滤波器组能量 mel_filter_bank = librosa.filters.mel(sr=sr, n_fft=frame_length, n_mels=40) mel_energies = np.dot(power_spectrum, mel_filter_bank.T) # 对能量取对数 log_mel_energies = np.log(mel_energies + 1e-10) # 加上一个小值以避免对数零 # 打印对数能量 print(log_mel_energies.shape) # (num_frames, n_mels) print(log_mel_energies) # 可视化第一个帧的对数梅尔能量 plt.figure(figsize=(10, 4)) plt.plot(log_mel_energies[0]) plt.title('Log Mel Energies of First Frame') plt.show()
在这个示例中:
• librosa.load 用于加载音频文件。
• librosa.util.frame 用于将音频信号分割成多个帧。
• np.hamming 用于生成汉明窗。
• np.fft.rfft 用于计算每个帧的快速傅里叶变换。
• np.abs 和平方操作用于计算功率谱。
• librosa.filters.mel 用于创建梅尔滤波器组。
• np.dot 用于计算梅尔滤波器组能量。
• np.log 用于对能量取对数。
7. 离散余弦变换 (DCT)
DCT 的目的是将对数梅尔滤波器组能量转换为倒谱系数,这些系数可以更好地表示音频信号的特征。DCT 的主要作用是将频域信息转换为倒谱域,并压缩特征向量的维度。
DCT 的步骤
1. 计算对数梅尔滤波器组能量:对每个帧的频谱应用梅尔滤波器组,计算每个滤波器的能量,并取对数。
2. 应用 DCT:对对数梅尔滤波器组能量应用离散余弦变换,得到倒谱系数。公式如下:

其中:
- c(n)c(n) 是第 nn 个梅尔频率倒谱系数。
- logP(k)\log P(k) 是第 kk 个梅尔滤波器的对数能量。
- KK 是梅尔滤波器的数量。
- nn 是倒谱系数的索引(通常取前 12 到 13 个系数)。
以下是一个使用 NWaves 库在 C# 中实现 DCT 的示例:
using System; using System.IO; using NWaves.Audio; using NWaves.FeatureExtractors; using NWaves.FeatureExtractors.Options; using NWaves.Transforms; using NWaves.Windows; class Program { static void Main(string[] args) { // 加载音频文件 var waveFile = new WaveFile(File.OpenRead("your_audio_file.wav")); var signal = waveFile[Channels.Left]; // 设置帧长度和帧移(以样本数表示) int frameSize = (int)(0.025 * signal.SamplingRate); // 25 毫秒的帧 int hopSize = (int)(0.01 * signal.SamplingRate); // 10 毫秒的帧移 // 分帧 var frames = signal.Frames(frameSize, hopSize); // 选择窗函数(例如汉明窗) var window = Window.OfType(WindowTypes.Hamming, frameSize); // 创建 FFT 变换器 var fft = new Fft(frameSize); // 创建梅尔滤波器组 var melFilterBank = FilterBanks.MelBank(40, frameSize, signal.SamplingRate, 0, signal.SamplingRate / 2); // 创建 DCT 变换器 var dct = new Dct(40); // 对每个帧应用窗函数并计算 FFT foreach (var frame in frames) { // 应用窗函数 for (int i = 0; i < frame.Length; i++) { frame[i] *= window[i]; } // 计算 FFT var spectrum = new float[frameSize]; fft.Magnitude(frame, spectrum); // 计算梅尔滤波器组能量 var melEnergies = new float[melFilterBank.Length]; for (int i = 0; i < melFilterBank.Length; i++) { melEnergies[i] = 0; for (int j = 0; j < melFilterBank[i].Length; j++) { melEnergies[i] += spectrum[j] * melFilterBank[i][j]; } } // 对能量取对数 for (int i = 0; i < melEnergies.Length; i++) { melEnergies[i] = (float)Math.Log(melEnergies[i] + 1e-10); // 加上一个小值以避免对数零 } // 计算 DCT var mfccs = new float[40]; dct.Direct(melEnergies, mfccs); // 打印 MFCCs Console.WriteLine(string.Join(", ", mfccs)); } } }
在这个示例中:
• WaveFile 类用于加载音频文件。
• Frames 方法用于将音频信号分割成多个帧。
• Window.OfType 方法用于生成指定类型的窗函数(例如汉明窗)。
• Fft 类用于创建 FFT 变换器。
• FilterBanks.MelBank 方法用于创建梅尔滤波器组。
• 对每个帧的频谱应用梅尔滤波器组,计算能量。
• 对每个滤波器的能量取对数。
• Dct 类用于创建 DCT 变换器,并计算离散余弦变换,得到 MFCCs。
以下是一个使用 scipy 库在 Python 中实现 DCT 的示例:
import numpy as np import librosa import scipy.fftpack import matplotlib.pyplot as plt # 读取音频文件 y, sr = librosa.load('your_audio_file.wav', sr=None) # 设置帧长度和帧移(以样本数表示) frame_length = int(0.025 * sr) # 25 毫秒的帧 hop_length = int(0.01 * sr) # 10 毫秒的帧移 # 分帧 frames = librosa.util.frame(y, frame_length=frame_length, hop_length=hop_length).T # 选择窗函数(例如汉明窗) window = np.hamming(frame_length) # 对每个帧应用窗函数 windowed_frames = frames * window # 计算每个帧的 FFT fft_frames = np.fft.rfft(windowed_frames, axis=1) # 计算功率谱 power_spectrum = np.abs(fft_frames) 2 # 创建梅尔滤波器组 n_mels = 40 mel_filter_bank = librosa.filters.mel(sr=sr, n_fft=frame_length, n_mels=n_mels) # 应用梅尔滤波器组 mel_energies = np.dot(power_spectrum, mel_filter_bank.T) # 对能量取对数 log_mel_energies = np.log(mel_energies + 1e-10) # 加上一个小值以避免对数零 # 应用 DCT mfccs = scipy.fftpack.dct(log_mel_energies, type=2, axis=1, norm='ortho') # 打印 MFCCs print(mfccs.shape) # (num_frames, n_mels) print(mfccs) # 可视化第一个帧的 MFCCs plt.figure(figsize=(10, 4)) plt.plot(mfccs[0]) plt.title('MFCCs of First Frame') plt.show()
在这个示例中:
• librosa.load 用于加载音频文件。
• librosa.util.frame 用于将音频信号分割成多个帧。
• np.hamming 用于生成汉明窗。
• np.fft.rfft 用于计算每个帧的快速傅里叶变换。
• np.abs 和平方操作用于计算功率谱。
• librosa.filters.mel 用于创建梅尔滤波器组。
• np.dot 用于应用梅尔滤波器组,计算每个滤波器的能量。
• np.log 用于对能量取对数。
• scipy.fftpack.dct 用于计算离散余弦变换,得到 MFCCs。
直接计算 MFCC
以上示例是为了详细演示每一个过程,而得益于预构建库的功能的封装,我们可以直接进行MFCC
以下是一个使用 librosa 库在 Python 中直接计算 MFCC 的示例:
import librosa import librosa.display import matplotlib.pyplot as plt # 加载音频文件 y, sr = librosa.load(r'your_audio_file.wav', sr=None) # 计算MFCC mfccs = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=13)
以下是一个使用 NWaves 库在 C# 中直接计算 MFCC 的示例:
using System; using System.IO; using NWaves.Audio; using NWaves.FeatureExtractors; using NWaves.FeatureExtractors.Options; class Program { static void Main(string[] args) { // 加载音频文件 var waveFile = new WaveFile(File.OpenRead("your_audio_file.wav")); var signal = waveFile[Channels.Left]; // 配置 MFCC 提取器 var mfccOptions = new MfccOptions { SamplingRate = signal.SamplingRate, FeatureCount = 13, // 通常使用 13 个系数 FrameDuration = 0.025, // 25 毫秒的帧 HopDuration = 0.01, // 10 毫秒的跳步 FilterbankSize = 26, // 滤波器组大小 LowFrequency = 0, HighFrequency = signal.SamplingRate / 2 }; var mfccExtractor = new MfccExtractor(mfccOptions); // 计算 MFCC var mfccs = mfccExtractor.ComputeFrom(signal); // 打印 MFCC foreach (var vector in mfccs) { Console.WriteLine(string.Join(", ", vector)); } } }
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/181387.html