在上一篇博文https://blog.csdn.net/zxy13826134783/article/details/105882490的基础上进一步研究,上一篇博客后面使用C#调用Speex是以文件名的方式进行数据传输,明显是不符合远程传输的要求,要实现远程传输,Speex必须能处理音频的字节数组,本文正是基于这种方式进行实现
必须在阅读C#封装C++基础上进行本文后面的操作:https://blog.csdn.net/zxy13826134783/article/details/105958311
注意:本文处理的wav音频文件必须是标准的wav文件,不然会出错
本文的Demo可以直接去我的仓库下载:https://gitee.com/zxy15914507674/shared_resource_name,找打对应的
speexdsp_Audio.rar下载即可
本文Demo的文件结构图:
进入SpeexWinproj文件夹后
为了减少代码的复杂度和业务逻辑,什么安全验证的都没有写进去,只突出核心的部分。
找到C++的项目,并找到SpeexWinProj.cpp文件,这正是C++实现降噪的源码,如下:
// SpeexWinProj.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "speex/speex_jitter.h"
#include "speex/speex_echo.h"
#include "speex/speex_preprocess.h"
#include "speex/speex_resampler.h"
#include
#include
#include
#include
#include
#include
#define SAMPLE_RATE (48000)
#define SAMPLES_PER_FRAME (1024)
SpeexPreprocessState *state ;
//初始化操作
void TestBuffer_init(){
state = speex_preprocess_state_init(1024, SAMPLE_RATE);
int denoise = 1;
int noiseSuppress = -25;
speex_preprocess_ctl(state, SPEEX_PREPROCESS_SET_DENOISE, &denoise);
speex_preprocess_ctl(state, SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, &noiseSuppress);
int i;
i = 0;
speex_preprocess_ctl(state, SPEEX_PREPROCESS_SET_AGC, &i);
i = 80000;
speex_preprocess_ctl(state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &i);
i = 0;
speex_preprocess_ctl(state, SPEEX_PREPROCESS_SET_DEREVERB, &i);
float f = 0;
speex_preprocess_ctl(state, SPEEX_PREPROCESS_SET_DEREVERB_DECAY, &f);
f = 0;
speex_preprocess_ctl(state, SPEEX_PREPROCESS_SET_DEREVERB_LEVEL, &f);
}
//传入字节数组的,每次处理1024个字节,返回是处理完毕的1024个字节,这样就可以远程传输了
void TestNoise_Buffer(byte *input_out)
{
speex_preprocess_run(state,(spx_int16_t*)input_out);
}
//释放
void TestBuffer_Destory(){
speex_preprocess_state_destroy(state);
}
找到C++项目中的SpeexWinProj.def文件,该文件定义导出的函数,如下:
LIBRARY BTREE
EXPORTS
TestNoise_Buffer
TestBuffer_init
TestBuffer_Destory
然后把C++项目生成,并把生成的dll文件拷贝到C#项目的Debug目录下测试,C#测试代码如下:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace CSharpCall
{
class Program
{
unsafe static void Main(string[] args)
{
FileStream fs = new FileStream("test1.wav", FileMode.Open);
byte[] fileBuffer = new byte[fs.Length];
fs.Read(fileBuffer, 0, fileBuffer.Length);
fs.Close();
AudioManager manager = new AudioManager();
manager.Init();
byte []outbuffer=manager.TestNoiseBuffer(fileBuffer);
FileStream fw = new FileStream("out1.wav", FileMode.Create);
fw.Write(outbuffer, 0, outbuffer.Length);
fw.Close();
Console.WriteLine("转换完成");
Console.ReadKey();
}
}
public class AudioManager {
//加载dll库,参数为dll库的名称,返回句柄
[DllImport("kernel32")]
public static extern IntPtr LoadLibrary(string lpFileName);
//通过句柄释放dll库
[DllImport("Kernel32")]
public static extern bool FreeLibrary(IntPtr handle);
//根据函数名输出库函数,返回函数的指针
[DllImport("Kernel32")]
public static extern IntPtr GetProcAddress(IntPtr handle, String funcname);
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
unsafe public delegate void TestBuffer_init_delegate();
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
unsafe public delegate void TestNoise_Buffer_delegate(byte[] in_out_put);
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
unsafe public delegate void TestBuffer_Destory_delegate();
TestNoise_Buffer_delegate TestNoise_Buffer;
TestBuffer_Destory_delegate TestBuffer_Destory;
///
/// 初始化操作
///
public void Init() {
//加载c++对应的dll库
IntPtr dll = LoadLibrary("SpeexWinProj.dll");
IntPtr TestBuffer_init_func = GetProcAddress(dll, "TestBuffer_init");
TestBuffer_init_delegate TestBuffer_init = (TestBuffer_init_delegate)Marshal.GetDelegateForFunctionPointer(TestBuffer_init_func, typeof(TestBuffer_init_delegate));
IntPtr TestNoise_Buffer_func = GetProcAddress(dll, "TestNoise_Buffer");
TestNoise_Buffer = (TestNoise_Buffer_delegate)Marshal.GetDelegateForFunctionPointer(TestNoise_Buffer_func, typeof(TestNoise_Buffer_delegate));
IntPtr TestBuffer_Destory_func = GetProcAddress(dll, "TestBuffer_Destory");
TestBuffer_Destory = (TestBuffer_Destory_delegate)Marshal.GetDelegateForFunctionPointer(TestBuffer_Destory_func, typeof(TestBuffer_Destory_delegate));
TestBuffer_init();
}
///
/// 音频降噪,处理字节数组
///
/// 输入的音频字节数组
/// 处理完毕后的字节数组
public byte[] TestNoiseBuffer(byte []inputBuffer){
byte[] outbuffer = new byte[inputBuffer.Length];
//前44个字节是头文件,不能做降噪处理,不然出现无法打开的情况
for (int i = 0; i < 44; i++)
{
outbuffer[i] = inputBuffer[i];
}
//采用速率
int sampleRate = 1024;
//每次处理的字节数组,每次处理1024个字节
byte[] input_out = new byte[sampleRate];
for (int i = 44; i < inputBuffer.Length - sampleRate; i = i + sampleRate)
{
//每次获取1024个字节
for (int j = 0; j < sampleRate; j++)
{
input_out[j] = inputBuffer[i + j];
}
//对1024个字节进行降噪操作
TestNoise_Buffer(input_out);
//把降噪后的1024个字节写入输出数组中
for (int j = 0; j < sampleRate; j++)
{
outbuffer[i + j] = input_out[j];
}
}
//释放资源
TestBuffer_Destory();
return outbuffer;
}
}
}
然后运行效果图如下图:
最后会在C#项目的Debug目录下生成降噪后的音频文件out1.wav,当然输入的test1.wav也没有噪声,因为有噪声的wav文件太难找了,就找了一个没有噪声的意思意思一下