前言:

早期的方法大多是基于声学特征的提取, 在时域上, 1975年, Rabiner 等人提出了基于短时能量和过零率的语音端点检测方法, 这是第一个系统而完整的语音端点检测算法。该方法共有三个门限值, 前两个是通过短时能量值来设置高、低两个门限, 进行端点位置的初判, 第三个是通过短时过零率值来设定, 并最终确定语音倾的起始点和终止点。该方法计算量小, 可以满足实时性的要求, 且在高信噪比 下具有较好的检测性能, 所以被广泛用于语音信号处理的各个领域。之后, 许多学者在这一方法的基础上又进行了一定的改进, 如基于绝对值能量的双门限检测算法,顾亚强等人提出了一种通过计算语音喊和噪音喊的过零率和短时能量的差分进行语音端点检测的方法, 陈振标等人提出了一种结合多子带能量特征和最优化边缘检测判决准则的算法。在频域上, Koba 等人通过对每一带噪语音顿分别进行快速傅立叶变换, 提出了一种基于频域信息的语音端点检测算法,这是第一个在频域上进行分析, 进而提取出相应特征的语音端点检测算法。之后, 提出了许多基于频域特征的语音端点检测算法, 如基于信息熵的语音端点检测算法、基于美尔倒谱的语音端点检测算法等。但无论是基于时域还是基于频域, 都有各自的优点与不足, 于是人们想要采用时频域相结合的方法, 更好的发挥各自的优点, 于是, Lin等人提出了增强时频 参数该参数包含了时域和频域两种信。但无论是基于时域、基于频域还是基于时频域相结合的方法, 它们在较高的信噪比下识别率较高, 但实际进行语音端点检测的环境是复杂多样的, 并且很难保证较高的信噪比。随后, 提出了基于模型匹配的语音端点检测方法, 这些方法通常包括训练阶段和测试阶段, 在训练阶段, 就像人学习知识一样, 让模型通过对训练语料的学习, 来获得模型参数, 在测试阶段, 只需将待划分语音与训练好的模型进行比对, 就可将其区分。隐马尔可夫模型(HMM) 最早发表在一些统计学论文中, 现在是语音识别技术中应用最广泛的一种模型。1998 年, 朱杰等人将HMM 模型应用于加入背景噪声的语音端点检测中, 首次提出了基于HMM 模型的语音端点检测算法。实验结果表明, 使用该方法来区分语音喊和噪音顿的准确率明显高于基于双门限的方法, 而且对于一些爆破音、鼻音和弱摩擦音等, 也很少出现丢失语音侦的现象。此外, 董恩清等人还将基于统计学理论的支持向量机(SVM) 应用于加噪的语音信号的端点检测方法中, 提出了基于SVM 的语音端点检测算法并验证了其有效性,但由于运算量较大, 该方法在训练阶段往往要花费很长的时间, 而在实际的分类中却计算量较小, 耗时较短。以上这些方法在较低的信噪比下仍可获得较高的准确率, 但也有其局限性, 首先, 由于模型中参数较少, 因此难以较好的描述数据, 其次, 这些方法都是从有限的数据集中估计参数, 可能不能充分的利用信息。近来, 由于基于神经网络的方法可以克服基于模型匹配方法的局限性, 并能取得较好的结果, 越来越多的人釆用这种学习方法进行语音端点检测, 包括深度信念网络等, 这极大的扩展了人们在这一领域的探索思路。但此类方法可能会因初始点的选取而陷入局部最优值, 从而影响语音端点检测的效果。


分析:

目前主流的VAD算法:

  1. 基于短时能量和过零率的语音端点检测算法
  2. 基于信息熵的语音端点检测算法
  3. 基于短时能频值的语音端点检测算法
  4. 基于神经网络的语音端点检测

经过分析,1、2、3  适用于在高性噪比环境下。在信噪比较低的环境下,检测效果不是太理想。而基于神经网络的方式,则在低信噪比的环境下,检测效果则优于1、2、3。

参考文章:

  1. https://www.docin.com/p-1537242532.html
  2. https://blog.csdn.net/ffmpeg4976/article/details/52349007
  3. https://blog.csdn.net/baienguon/article/details/80539296

项目背景:

      由于公司项目采用C#开发 ,所以本文实现思路采用  C# 调用python训练模型进行语音端点检测。

知识储备:(分享一些我学习的音频方面的文章)

  1. 音频属性:https://blog.csdn.net/aoshilang2249/article/details/38469051
  2. MFCC详解:https://blog.csdn.net/suan2014/article/details/82021324
  3. 傅里叶转换:https://blog.csdn.net/u011947630/article/details/81513075
  4. 还有一些忘记收藏了。总之遇到不明白的地方多查查。

实现思路:

  1. 采集噪声和人声两个正负样本的音频文件,使用Python 通过提取MFCC特征值来训练,保存模型文件。
  2. 由于我采用的是基于Keras LSTM 来训练的 ,但Keras 没有提供C#接口 所以只能将模型转成.pb文件。然后使用TensorflowSharp来调用.pb文件来预测结果。
  3. (C#)我设定我采集的时间间隔为1s,移动的幅度为500ms。
  4. 首先采集1s的音频信息,然后提取1s音频的MFCC特征值进行预测,如果预测结果为人声,则表示为声音的起点,将该段音频保存下来,如果为噪声,则舍弃。然后移动500ms,在提取1s的音频MFCC特征值进行预测,如果是人声并上一个也是人声,则将音频偏移的500ms音频保存下来,若上一个是噪声,则表示该音频为起点。总之就这样以此类推,最后会检测出一段符合模型特征的人声音频。
  5. 以上就是我实现VAD的大概思路,可能不是很完美,但也是我想到的最好解决的办法了。毕竟能力有限啊 ,如果各位大佬有什么好的解决办法,欢迎来指导一二,小弟感激不尽!!

代码:

1.Python 训练样本模型    基于 Kersa LSTM:

引用:https://github.com/Renovamen/Speech-Emotion-Recognition

https://github.com/hcmlab/vadnet

训练样本音频 : 16k 16bit

部分主要代码:(由于代码较多,所以只贴出主要实现代码)

from keras.utils import np_utils
from DNN_Model import LSTM_Model
from Utilities import get_dataDATA_PATH = 'DataSet/'CLASS_LABELS = ("hg", "nd", "pz")def train():FLATTEN = FalseNUM_LABELS = len(CLASS_LABELS)SVM = Falsex_train, x_test, y_train, y_test = get_data(DATA_PATH, class_labels=CLASS_LABELS, flatten=FLATTEN, _svm=SVM)y_train = np_utils.to_categorical(y_train)y_test_train = np_utils.to_categorical(y_test, len(CLASS_LABELS))print('-------------------------------- LSTM Start --------------------------------')model = LSTM_Model(input_shape=x_train[0].shape, num_classes=NUM_LABELS)model.train(x_train, y_train, x_test, y_test_train, n_epochs=100)model.evaluate(x_test, y_test)model.save_model("LSTM1")print('-------------------------------- LSTM End --------------------------------')train()

读取数据和提取mfcc特征值

# 从给定文件夹读取数据和提取MFCC特征import os
import sys
from typing import Tuple
import numpy as np
import scipy.io.wavfile as wav
from sklearn.model_selection import train_test_split
from keras.models import model_from_json
from sklearn.externals import joblib
from MFCC_COM import get_mfcc
import scipy.io.wavfilemean_signal_length = 16000"""
get_feature(): 提取某个音频的MFCC特征向量输入:file_path(str): 该音频路径mfcc_len(int): 每帧的MFCC特征数flatten(bool): 是否降维数据输出:numpy.ndarray: 该音频的MFCC特征向量
"""def get_feature(file_path: str, mfcc_len: int = 39, flatten: bool = False):# 某些音频用scipy.io.wavfile读会报 "Incomplete wav chunk" error# 似乎是因为scipy只能读pcm和float格式,而有的wav不是这两种格式...# fs, signal = wav.read(file_path)# signal, fs = librosa.load(file_path, 16000)fs, signal = scipy.io.wavfile.read(file_path)s_len = len(signal)# 如果音频信号小于mean_signal_length,则扩充它if s_len < mean_signal_length:pad_len = mean_signal_length - s_lenpad_rem = pad_len % 2pad_len //= 2signal = np.pad(signal, (pad_len, pad_len + pad_rem), 'constant', constant_values=0)# 否则把它切开else:pad_len = s_len - mean_signal_lengthpad_len //= 2signal = signal[pad_len:pad_len + mean_signal_length]mel_coefficients = get_dll(signal, fs, mfcc_len)#  用 SVM & MLP 模型时要降维数据if flatten:mel_coefficients = np.ravel(mel_coefficients)return mel_coefficientsdef get_feature_result(signal, fs, mfcc_len: int = 39, flatten: bool = False):# 某些音频用scipy.io.wavfile读会报 "Incomplete wav chunk" error# 似乎是因为scipy只能读pcm和float格式,而有的wav不是这两种格式...# fs, signal = wav.read(file_path)# signal, fs = librosa.load(file_path)s_len = len(signal)# 如果音频信号小于mean_signal_length,则扩充它if s_len < mean_signal_length:pad_len = mean_signal_length - s_lenpad_rem = pad_len % 2pad_len //= 2signal = np.pad(signal, (pad_len, pad_len + pad_rem), 'constant', constant_values=0)# 否则把它切开else:pad_len = s_len - mean_signal_lengthpad_len //= 2signal = signal[pad_len:pad_len + mean_signal_length]mel_coefficients = get_mfcc(signal, fs, mfcc_len)# mel_coefficients = librosa.feature.mfcc(signal, fs,  n_mfcc=39)#  用 SVM & MLP 模型时要降维数据if flatten:mel_coefficients = np.ravel(mel_coefficients)return mel_coefficientsdef get_dll(signal, fs, mfcc_len):mfcc = get_mfcc(signal, fs, mfcc_len)mfcc = mfcc.Arrayarray = []for _ in range(mfcc.Length):array.append(mfcc[_])return np.array(array).reshape(-1, mfcc_len)def get_data(data_path: str, mfcc_len: int = 39,class_labels: Tuple = ("angry", "fear", "happy", "neutral", "sad", "surprise"), flatten: bool = False,_svm: bool = False):data = []labels = []cur_dir = os.getcwd()sys.stderr.write('Curdir: %s\n' % cur_dir)os.chdir(data_path)# 遍历文件夹for i, directory in enumerate(class_labels):sys.stderr.write("Started reading folder %s\n" % directory)os.chdir(directory)# 读取该文件夹下的音频for filename in os.listdir('.'):if not filename.endswith('wav'):continuefilepath = os.getcwd() + '/' + filename# 提取该音频的特征向量feature_vector = get_feature(file_path=filepath, mfcc_len=mfcc_len, flatten=flatten)data.append(feature_vector)labels.append(i)sys.stderr.write("Ended reading folder %s\n" % directory)os.chdir('..')os.chdir(cur_dir)# 划分训练集和测试集x_train, x_test, y_train, y_test = train_test_split(np.array(data), np.array(labels), test_size=0.001,random_state=42)return np.array(x_train), np.array(x_test), np.array(y_train), np.array(y_test)def get_data(data_path: str, mfcc_len: int = 39,class_labels: Tuple = ("angry", "fear", "happy", "neutral", "sad", "surprise"), flatten: bool = False,_svm: bool = False):data = []labels = []cur_dir = os.getcwd()sys.stderr.write('Curdir: %s\n' % cur_dir)os.chdir(data_path)# 遍历文件夹for i, directory in enumerate(class_labels):sys.stderr.write("Started reading folder %s\n" % directory)os.chdir(directory)# 读取该文件夹下的音频for filename in os.listdir('.'):if not filename.endswith('wav'):continuefilepath = os.getcwd() + '/' + filename# 提取该音频的特征向量feature_vector = get_feature(file_path=filepath, mfcc_len=mfcc_len, flatten=flatten)data.append(feature_vector)labels.append(i)sys.stderr.write("Ended reading folder %s\n" % directory)os.chdir('..')os.chdir(cur_dir)# 划分训练集和测试集x_train, x_test, y_train, y_test = train_test_split(np.array(data), np.array(labels), test_size=0.001,random_state=42)return np.array(x_train), np.array(x_test), np.array(y_train), np.array(y_test)'''
load_model_dnn(): 加载 CNN & LSTM 的模型输入:model_name(str): 模型名称load_model(str): 模型种类(DNN / ML)输出:model: 加载好的模型
'''def load_model(model_name: str, load_model: str):if load_model == 'DNN':# 加载jsonmodel_path = 'Models/' + model_name + '.h5'model_json_path = 'Models/' + model_name + '.json'json_file = open(model_json_path, 'r')loaded_model_json = json_file.read()json_file.close()model = model_from_json(loaded_model_json)# 加载权重model.load_weights(model_path)elif load_model == 'ML':model_path = 'Models/' + model_name + '.m'model = joblib.load(model_path)return model

Model:

# CNN & LSTM
import sys
import numpy as np
from keras import Sequential
from keras.layers import LSTM as KERAS_LSTM, Dense, Dropout, Conv2D, Flatten, BatchNormalization, Activation, \MaxPooling2D
from Common_Model import Common_Model# class CNN 和 class LSTM 继承了此类(实现了make_model方法)
class DNN_Model(Common_Model):'''__init__(): 初始化神经网络输入:input_shape(tuple): 张量形状num_classes(int): 标签种类数量'''def __init__(self, input_shape, num_classes, **params):super(DNN_Model, self).__init__(**params)self.input_shape = input_shapeself.model = Sequential()self.make_model()self.model.add(Dense(num_classes, activation='softmax'))self.model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])print(self.model.summary(), file=sys.stderr)'''save_model(): 将模型权重以 model_name.h5 和 model_name.json 命名存储在 /Models 目录下'''def save_model(self, model_name):h5_save_path = 'Models/' + model_name + '.h5'self.model.save_weights(h5_save_path)save_json_path = 'Models/' + model_name + '.json'with open(save_json_path, "w") as json_file:json_file.write(self.model.to_json())'''train(): 在给定训练集上训练模型输入:x_train (numpy.ndarray): 训练集样本y_train (numpy.ndarray): 训练集标签x_val (numpy.ndarray): 测试集样本y_val (numpy.ndarray): 测试集标签n_epochs (int): epoch数'''def train(self, x_train, y_train, x_val=None, y_val=None, n_epochs=50):best_acc = 0if x_val is None or y_val is None:x_val, y_val = x_train, y_trainfor i in range(n_epochs):# 每个epoch都随机排列训练数据p = np.random.permutation(len(x_train))x_train = x_train[p]y_train = y_train[p]'''fit( x, y, batch_size=32, epochs=10, verbose=1, callbacks=None,validation_split=0.0, validation_data=None, shuffle=True, class_weight=None, sample_weight=None, initial_epoch=0)x:输入数据。如果模型只有一个输入,那么x的类型是numpy array,如果模型有多个输入,那么x的类型应当为list,list的元素是对应于各个输入的numpy arrayy:标签,numpy arraybatch_size:整数,指定进行梯度下降时每个batch包含的样本数。训练时一个batch的样本会被计算一次梯度下降,使目标函数优化一步。epochs:整数,训练终止时的epoch值,训练将在达到该epoch值时停止,当没有设置initial_epoch时,它就是训练的总轮数,否则训练的总轮数为epochs - inital_epochverbose:日志显示,0为不在标准输出流输出日志信息,1为输出进度条记录,2为每个epoch输出一行记录callbacks:list,其中的元素是keras.callbacks.Callback的对象。这个list中的回调函数将会在训练过程中的适当时机被调用,参考回调函数validation_split:0~1之间的浮点数,用来指定训练集的一定比例数据作为验证集。验证集将不参与训练,并在每个epoch结束后测试的模型的指标,如损失函数、精确度等。注意,validation_split的划分在shuffle之前,因此如果你的数据本身是有序的,需要先手工打乱再指定validation_split,否则可能会出现验证集样本不均匀。validation_data:形式为(X,y)的tuple,是指定的验证集。此参数将覆盖validation_spilt。shuffle:布尔值或字符串,一般为布尔值,表示是否在训练过程中随机打乱输入样本的顺序。若为字符串“batch”, 则是用来处理HDF5数据的特殊情况,它将在batch内部将数据打乱。class_weight:字典,将不同的类别映射为不同的权值,该参数用来在训练过程中调整损失函数(只能用于训练)            sample_weight:权值的numpy array,用于在训练时调整损失函数(仅用于训练)。可以传递一个1D的与样本等长的向量用于对样本进行1对1的加权,或者在面对时序数据时,传递一个的形式为(samples,sequence_length)的矩阵来为每个时间步上的样本赋不同的权。这种情况下请确定在编译模型时添加了sample_weight_mode=’temporal’。            initial_epoch: 从该参数指定的epoch开始训练,在继续之前的训练时有用。fit函数返回一个History的对象,其History.history属性记录了损失函数和其他指标的数值随epoch变化的情况,如果有验证集的话,也包含了验证集的这些指标变化情况'''self.model.fit(x_train, y_train, batch_size=32, epochs=1)# 训练过程的损失率变化图# 计算损失率和准确率loss, acc = self.model.evaluate(x_val, y_val)if acc > best_acc:best_acc = accprint("TRAIN:%d /  %d" % (i, n_epochs))self.trained = True'''recognize_one(): 识别某个音频的情感输入:sample: 要预测的样本输出:预测结果,置信概率(int, numpy.ndarray)'''def recognize_one(self, sample):# 没有训练和加载过模型if not self.trained:sys.stderr.write("No Model.")sys.exit(-1)return np.argmax(self.model.predict(np.array([sample]))), self.model.predict(np.array([sample]))[0]def make_model(self):raise NotImplementedError()class CNN_Model(DNN_Model):def __init__(self, **params):params['name'] = 'CNN'super(CNN_Model, self).__init__(**params)def make_model(self):self.model.add(Conv2D(8, (13, 13), input_shape=(self.input_shape[0], self.input_shape[1], 1)))self.model.add(BatchNormalization(axis=-1))self.model.add(Activation('relu'))self.model.add(Conv2D(8, (13, 13)))self.model.add(BatchNormalization(axis=-1))self.model.add(Activation('relu'))self.model.add(MaxPooling2D(pool_size=(2, 1)))self.model.add(Conv2D(8, (13, 13)))self.model.add(BatchNormalization(axis=-1))self.model.add(Activation('relu'))self.model.add(Conv2D(8, (2, 2)))self.model.add(BatchNormalization(axis=-1))self.model.add(Activation('relu'))self.model.add(MaxPooling2D(pool_size=(2, 1)))self.model.add(Flatten())self.model.add(Dense(64))self.model.add(BatchNormalization())self.model.add(Activation('relu'))self.model.add(Dropout(0.2))class LSTM_Model(DNN_Model):def __init__(self, **params):params['name'] = 'LSTM'super(LSTM_Model, self).__init__(**params)def make_model(self):#self.model.add(KERAS_LSTM(128, input_shape=(self.input_shape[0], self.input_shape[1])))self.model.add(Dropout(0.5))self.model.add(Dense(32, activation='relu'))  # 标准的一维全连接层self.model.add(Dense(16, activation='tanh'))

2.python可以通过librosa和scipy来获取mfcc特征值,但我发现C#获取的mfcc特征值和python获取的mfcc特征值不同,也就无法准确的预测结果(暂时不清楚原因,公式太过复杂,有明白的大佬,欢迎指导下)。我想的办法是通过python调用C#dll来获取MFCC

C#:编译成dll

引用:https://github.com/ar1st0crat/NWaves

using NumSharp;
using NWaves.FeatureExtractors;
using NWaves.FeatureExtractors.Base;
using System.Collections.Generic;namespace MFCC
{public class MYMFCC{public NDArray GetMFCC(float[] input, int sr, int mfcc_size){var mfccExtractor = new MfccExtractor(sr, mfcc_size);var mfccVectors = mfccExtractor.ComputeFrom(input);List<float> result = new List<float>();foreach (FeatureVector vector in mfccVectors){foreach (float _ in vector.Features){result.Add(_);}}return np.array(result.ToArray());}}
}
Python: (引用相关的dll)
import clrclr.FindAssembly("MFCCSharp.dll")
clr.FindAssembly("NWaves.dll")
clr.FindAssembly("NumSharp.dll")
from MFCC import *
from NWaves import *
from NumSharp import *instance = MYMFCC()def get_mfcc(input, sr, mfcc_len):instance = MYMFCC()return instance.GetMFCC(input, sr, mfcc_len)

3.由于我 python训练的模型是基于keras的,但keras没有提供c#接口。所有现将keras生成的模型.h5文件转换成.pb文件。通过tensorflowsharp来调用.pb文件。

python:( .h5 -> .pb)

引用:https://blog.csdn.net/qq_25109263/article/details/81285952

from keras.models import load_model
import tensorflow as tf
from keras import backend as K, Sequential
from tensorflow.python.framework import graph_io
from keras.models import model_from_jsondef freeze_session(session, keep_var_names=None, output_names=None, clear_devices=True):from tensorflow.python.framework.graph_util import convert_variables_to_constantsgraph = session.graphwith graph.as_default():freeze_var_names = list(set(v.op.name for v in tf.global_variables()).difference(keep_var_names or []))output_names = output_names or []output_names += [v.op.name for v in tf.global_variables()]input_graph_def = graph.as_graph_def()if clear_devices:for node in input_graph_def.node:node.device = ""frozen_graph = convert_variables_to_constants(session, input_graph_def,output_names, freeze_var_names)return frozen_graphdef load_model():model_path = 'Models/LSTM1.h5'model_json_path = 'Models/LSTM1.json'json_file = open(model_json_path, 'r')loaded_model_json = json_file.read()json_file.close()model = model_from_json(loaded_model_json)# 加载权重model.load_weights(model_path)return model"""----------------------------------配置路径-----------------------------------"""
epochs = 20
h5_model_path = 'Models/LSTM1.h5'
output_path = 'PBModels'
pb_model_name = 'LSTM1.pb'"""----------------------------------导入keras模型------------------------------"""
K.set_learning_phase(0)net_model = load_model()print('input is :', net_model.input.name)
print('output is:', net_model.output.name)"""----------------------------------保存为.pb格式------------------------------"""
sess = K.get_session()
frozen_graph = freeze_session(K.get_session(), output_names=[net_model.output.op.name])
graph_io.write_graph(frozen_graph, output_path, pb_model_name, as_text=False)

4.使用C#调用python训练的模型来实现预测。

类库:TensorflowSharp (tensorflow) , NumSharp (numpy) , NWave (获取MFCC)

using NumSharp;
using NWaves.FeatureExtractors;
using NWaves.FeatureExtractors.Base;
using System;
using System.Collections.Generic;
using System.IO;
using TensorFlow;namespace TensorflowSharpDemo
{class VoiceTest{private static String[] CLASS_LABELS = new string[] { "anjian", "di", "huanjie", "mingdi", "pengzhuang", "qita", "shebeiyuyin", "voice" };private static int VOICE_INDEX = 7;private static int FRAME_RATE = 16000;private static int FRAME_MOVE = 8000;private static TFGraph graph;private static TFSession session;public static void test(){//CLASS_LABELS = ("anjian", "di", "huanjie", "mingdi", "pengzhuang", "qita", "shebeiyuyin", "voice")graph = new TFGraph();graph.Import(File.ReadAllBytes("../../model/LSTM1.pb"), "");session = new TFSession(graph);List<float> audios = new WAVReader().ReadWAVFile("../../test/123.wav");NDArray input = audioToFrames(audios.ToArray(), 16000);logits(input, "");}private static float[] get_feature_result(float[] input){float[] mfcc = getMfcc(input);return mfcc;}/// <summary>///  帧移移动音频帧,补0/// </summary>private static float[] padd(float[] input, int frameMove){int con = input.Length % frameMove;if (con == 0)return input;int dis = frameMove - con;List<float> coll = new List<float>();int b_len = dis / 2;for (int i = 0; i < b_len; i++){coll.Add(0);}for (int i = 0; i < input.Length; i++){coll.Add(input[i]);}int e_len = dis - b_len;for (int i = 0; i < b_len; i++){coll.Add(0);}return coll.ToArray();}/// <summary>/// /// </summary>/// <param name="input">音频</param>/// <param name="frameLen">帧长</param>/// <param name="frameMove">帧移</param>/// <returns></returns>private static float[] fragment(float[] input, int frameLen, int frameMove){input = padd(input, frameMove);List<float> frames = new List<float>();int n_step = 0;while (n_step * frameMove + frameLen <= input.Length){for (int k = n_step * frameMove; k < n_step * frameMove + frameLen; k++){frames.Add(input[k]);}n_step++;}return frames.ToArray();}private static NDArray audioToFrames(float[] input, int frame){input = fragment(input, FRAME_RATE, FRAME_MOVE);int row = input.Length / FRAME_RATE;NDArray array = np.array(input).reshape(row, FRAME_RATE);return array;}private static NDArray predict_model(float[] input, int sr){var mfcc = get_feature_result(input);int ax = mfcc.Length / 39;var tensor = TFTensor.FromBuffer(new TFShape(1, ax, 39), mfcc, 0, mfcc.Length);var runner = session.GetRunner();runner.AddInput(graph["lstm_1_input"][0], tensor).Fetch(graph["dense_3/Softmax"][0]);var output = runner.Run();var result = output[0];int result_count = ((float[][])result.GetValue(jagged: true)).Length;List<float> resultColl = new List<float>();for (int i = 0; i < result_count; i++){float[] a = ((float[][])result.GetValue(jagged: true))[i];string s = null;for (int j = 0; j < a.Length; j++){resultColl.Add(a[j]);}}return np.array(resultColl.ToArray()).reshape(1, CLASS_LABELS.Length);}private static void logits(NDArray input, string outFileDir){int length = input.shape[0];int n_step = 0;List<NDArray> label = new List<NDArray>();while (n_step < length){float[] temp = (float[])input[n_step].Array;NDArray result = predict_model(temp, FRAME_RATE);label.Add(result);n_step++;}List<float[]> voice = new List<float[]>();List<float> voice_temp = new List<float>();int noiseCount = 0;for (int i = 0; i < length; i++){int index = np.argmax(label[i]);float[] sound = (float[])input[i].Array;if (index == VOICE_INDEX){noiseCount = 0;//上一帧是噪音if (voice_temp.Count == 0){voice_temp = floatToAddList(sound, voice_temp);continue;}//上一帧是人声voice_temp = floatToAddList(sound, voice_temp, FRAME_MOVE);continue;}else{noiseCount++;// 连续检测超过一帧(自定)都是噪音,则表示检测出一段完整语音 //if (noiseCount >= FRAME_RATE / FRAME_MOVE)//{//    if (voice_temp.Count > 0)//        voice.Add(voice_temp.ToArray());//    voice_temp = new List<float>();//    noiseCount = 0;//}}}if (voice_temp.Count > 0){voice.Add(voice_temp.ToArray());}for (int j = 0; j < voice.Count; j++){toSaveAudio(voice[j], "voice" + j);}}private static List<float> floatToAddList(float[] audio, List<float> coll){foreach (float _f in audio){coll.Add(_f);}return coll;}private static List<float> floatToAddList(float[] audio, List<float> coll, int frameMove){int index = 0;foreach (float _f in audio){if (index >= audio.Length - frameMove)coll.Add(_f);index++;}return coll;}private static void toSaveAudio(float[] audio, string name){string audioFileName = @"D:\WorkSpace\VS\TensorflowSharpDemo\TensorflowSharpDemo\voice\" + name + ".wav";WaveSaveHelper.Save(audioFileName, audio);}/// <summary>///   获取MFCC/// </summary>private static float[] getMfcc(float[] input){var mfccExtractor = new MfccExtractor(16000, 39);var mfccVectors = mfccExtractor.ComputeFrom(input);List<float> result = new List<float>();foreach (FeatureVector vector in mfccVectors){foreach (float _ in vector.Features){result.Add(_);}}return result.ToArray();}}
}

问题:

1. python 通过libroas或scripy获取mfcc特征值和C#实现的获取mfcc特征值不同。所以导致C#调用模型预测的结果不正确。我的解决办法是通过C#编写一个获取mfcc特征值的dll(基于NWave.dll https://github.com/ar1st0crat/NWaves),然后使用python来调用。(没有办法的办法,这个地方搞了好几天天也没搞懂,也可能是我哪个地方整错了,总之如果有明白这地方的大佬,欢迎来赐教,小弟万分感谢。)

2.通过测试发现,检测出的音频多多少少还是会存在一些噪音的,不过我感觉效果还是可以接受的。

3.还在努力优化和测试中。。。  = = 、

总结:

通过最近一段时间的学习和努力,初步实现了端点检测的功能,不过效果还有待提高,我会继续努力的,要什么新的体会,我回及时和大家分享出来的。由于刚刚接触音频这一块,还是小白一枚,有太多的地方理解的不是非常透彻,可以说只是略知一二,所以有一些表述不正确的地方,还请大家多多见谅,也希望大家能提出你们宝贵的意见和指正,小弟感激不尽!!!!

下面是我写的demo,有想看的可以下下来看一看,demo中的没有音频文件,下载后需把自己训练的音频文件对应文件夹名字存放,然后训练出模型文件,再转成.pb文件,最后将pb文件复制到c#项目中,供c#调用。(表达能力有限,如果有不是太明白的地方可以留言。  = =、)

demo地址:https://download.csdn.net/download/haiyangyunbao813/11155896

C# 语音端点检测(VAD)实现过程分析相关推荐

  1. python分割语音端点检测_python的webrtc库如何实现语音端点检测 科大讯飞输入法PC体验版下载:语音+手写+...

    python的webrtc库如何实现语音端点检测 科大讯飞输入法PC体验版下载:语音+手写+ 2018-03-05 出处:网络 整理:zhishizhan.net 延伸:科大讯飞输入法PC体验版下载: ...

  2. python分割语音端点检测_如何实现语音端点检测

    跪CSS布局HTML小编今天和大家分享一篇毕业论文 题目<语音端点检测与分割研究方欢迎来到CSS布局HTML的.你要的相关资料我有啊. 我给你发到了附件中. 楼上的复制的我的回答没有把实际的东西 ...

  3. 语音端点检测 matlab 论文,基于MATLAB的语音端点检测

    求助,哪位高手帮忙看看以下程序全不? 基于Matlab编写的语音端点检测程序 function [x1,x2] = vad(x) %幅度归一化到[-1,1] x = double(x); x = x ...

  4. 语音端点检测(1):双门限法(简单教学版)

    为什么要有语音端点检测?或者换个角度说,静默检测.静音检测. 以下摘自百度. 语音活动检测(Voice Activity Detection,VAD)又称语音端点检测,语音边界检,是指在噪声环境中检测 ...

  5. 小波变换学习~语音端点检测

    参考: 现代语音信号处理,p97 Precise detection of speech endpoints dynamically: A wavlet convolution based appro ...

  6. 【语音识别】语音端点检测及Python实现

    [语音识别]语音端点检测及Python实现 一.语音信号的分帧处理 二.端点检测方法 2.1.短时能量 2.2.短时过零率 三.Python实现 从接收的语音信号中准确检测出人声开始和结束的端点是进行 ...

  7. matlab浊音段和清音段,基于Matlab编写的语音端点检测1

    wavread 基于Matlab编写的语音端点检测 专业: 班级: 姓名: 指导教师: 2011年6月18日 一.实验目的 1.学会MATLAB的使用,掌握MATLAB的程序设计方法: 3.掌握语音处 ...

  8. MATLAB语音端点检测

    第一章 绪论 Matlab是矩阵实验室(Matrix Laboratory)的简称,是美国MathWorks公司出品的商业数学软件,用于算法开发.数据可视化.数据分析以及数值计算的高级技术计算语言和交 ...

  9. 语音端点检测 php,几种语音端点检测方法简介

    几种语音端点检测方法简介 2011年第11期福建电脑 67 几种语音端点检测方法简介 邢亚从 (苏州市职业大学江苏苏州215000) [摘要]:语音的端点检测在语音的编码.语音识别.语音增强.说话人识 ...

最新文章

  1. 2021年最后几天,使用SSM实现网上购物商城系统
  2. Laravel-数据库操作笔记
  3. 计算机编程工程师理论知识,结构工程师基础知识点:程序设计语言
  4. 火眼睛睛查coredump(stl sort)------永远让比较函数对相同元素返回false
  5. [Kaggle] Digit Recognizer 手写数字识别(卷积神经网络)
  6. 归并排序(视频+详解+代码)
  7. INF文件修改注册表
  8. iso qemu 安装ubuntu_我该如何安装qemu?
  9. String---Double 不依赖地域性的转换
  10. mysql hash切分_轻松优化MySQL-之数据库切分1
  11. PopupWindow 常用方法学习笔记
  12. 【Iphone 游戏开发之一】创建视图并绘制简单图形
  13. 用matlab如何画六边形,matlab怎样直接画出六边形
  14. 作为第三代互联网,Web3 的理念还未被真正阐述
  15. ElasticSearch 图片搜索插件 (一)
  16. CREO:CREO软件之零件【工具(调查/模型意图/实用工具)】、【视图(可见性/方向/模型显示/显示/窗口)】的简介及其使用方法(图文教程)之详细攻略
  17. Python uniform() 函数
  18. 微信小程序 - 一键复制功能
  19. DOTS介绍+Unity DOTS-MAN小游戏项目实战
  20. asp.net mvc Html.BeginForm()用法

热门文章

  1. 苹果Arm芯片适配开发 (Apple Silicon)
  2. 使用VM安装安卓虚拟机
  3. EventLog Analyzer:高效保护网络安全的强大工具
  4. Terracotta 3.2.1简介 (二)
  5. 11 计算机组成原理第七章 输入/输出系统 I/O系统基本概念 外部设备
  6. 计算机械结构变形,机械结构设计-力学原理设计准则
  7. STM32F10xxx20xxx21xxxL1xxxx Cortex-M3程序设计手册 阅读笔记二(5):Cortex-M3处理器能量管理
  8. 数学公式中一对双竖线
  9. 北京软件项目外包流程及管理
  10. REVIT建模步骤中:绘制形状不能拾取两条参照平面的交点解决方法