在上一篇博文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 <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <windows.h>
#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;
/// <summary>
/// 初始化操作
/// </summary>
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();
}
/// <summary>
/// 音頻降噪,處理字節數組
/// </summary>
/// <param name="inputBuffer">輸入的音頻字節數組</param>
/// <returns>處理完畢後的字節數組</returns>
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文件太難找了,就找了一個沒有噪聲的意思意思一下