在.NET中使用Speex -- 音频数据编解码

Speex是一套开源的音频编解码库,最新版本还包含了回音消除和防抖动等功能,如果我们想开发语音聊天或视频会议这样的系统,Speex将是一个不错的选择。到 http://www.speex.org可以下载Speex的源码(编译后的dll为libspeex.dll),最新版本为1.2。不过源码是用C++开发的,直接在.NET中使用会有诸多不便,为此,我用C#将其封装,使得编解码的调用相当简单。

  由于Speex原始导出的API不是很方便C#调用,所以,在用C#封装之前,先要用C++对Speex的原始API进行简化,新建一个名为Speex的VC项目,然后引用libspeex.dll的相关库文件,添加cpp文件后,复制下列源码到文件中:

#include "speex\speex.h"

#include <windows.h>

#include <stdio.h>

#include <stdlib.h>

#include "speex/speex_echo.h"

#include "speex/speex_preprocess.h"

#include "Speex.h"

#define FRAME_SIZE 160

float encoder_input[FRAME_SIZE];

void *encoder_state;

SpeexBits encoder_bits;

BOOL APIENTRY DllMain( HANDLE hModule,

DWORD  ul_reason_for_call,

LPVOID lpReserved

)

{

return TRUE;

}

extern "C" __declspec(dllexport) void encoder_init(int quality)

{

encoder_state = speex_encoder_init(&speex_nb_mode);

speex_encoder_ctl(encoder_state, SPEEX_SET_QUALITY, &quality);

speex_bits_init(&encoder_bits);

}

extern "C" __declspec(dllexport) void encoder_dispose()

{

speex_encoder_destroy(encoder_state);

speex_bits_destroy(&encoder_bits);

}

extern "C" __declspec(dllexport) int encoder_encode(const short *data, char *output)

{

for (int i = 0; i < FRAME_SIZE; i++)

encoder_input[i] = data[i];

speex_bits_reset(&encoder_bits);

speex_encode(encoder_state, encoder_input, &encoder_bits);

return speex_bits_write(&encoder_bits, output, 200);

}

float decoder_output[FRAME_SIZE];

void *decoder_state;

SpeexBits decoder_bits;

extern "C" __declspec(dllexport) void decoder_init()

{

decoder_state = speex_decoder_init(&speex_nb_mode);

int tmp = 1;

speex_decoder_ctl(decoder_state, SPEEX_SET_ENH, &tmp);

speex_bits_init(&decoder_bits);

}

extern "C" __declspec(dllexport) void decoder_dispose()

{

speex_decoder_destroy(decoder_state);

speex_bits_destroy(&decoder_bits);

}

extern "C" __declspec(dllexport) void decoder_decode(int nbBytes, char *data, short *output)

{

speex_bits_read_from(&decoder_bits, data, nbBytes);

speex_decode(decoder_state, &decoder_bits, decoder_output);

for (int i = 0; i < FRAME_SIZE; i++)

{

output[i] = decoder_output[i];

}

}

/***************************************************  回音消除 **************************************/

bool      m_bSpeexEchoHasInit;

SpeexEchoState*   m_SpeexEchoState;

SpeexPreprocessState* m_pPreprocessorState;

int      m_nFilterLen;

int      m_nSampleRate;

float*   m_pfNoise;

extern "C" __declspec(dllexport) void SpeexEchoCapture(short* input_frame, short* output_frame)

{

speex_echo_capture(m_SpeexEchoState, input_frame, output_frame);

}

extern "C" __declspec(dllexport) void SpeexEchoPlayback(short* echo_frame)

{

speex_echo_playback(m_SpeexEchoState, echo_frame);

}

extern "C" __declspec(dllexport) void SpeexEchoReset()

{

if (m_SpeexEchoState != NULL)

{

speex_echo_state_destroy(m_SpeexEchoState);

m_SpeexEchoState = NULL;

}

if (m_pPreprocessorState != NULL)

{

speex_preprocess_state_destroy(m_pPreprocessorState);

m_pPreprocessorState = NULL;

}

if (m_pfNoise != NULL)

{

delete []m_pfNoise;

m_pfNoise = NULL;

}

m_bSpeexEchoHasInit = false;

}

extern "C" __declspec(dllexport) void SpeexEchoInit(int filter_length, int sampling_rate ,bool associatePreprocesser)

{

SpeexEchoReset();

if (filter_length<=0 || sampling_rate<=0)

{

m_nFilterLen  = 160*8;

m_nSampleRate = 8000;

}

else

{

m_nFilterLen  = filter_length;

m_nSampleRate = sampling_rate;

}

m_SpeexEchoState = speex_echo_state_init(FRAME_SIZE, m_nFilterLen);

m_pPreprocessorState = speex_preprocess_state_init(FRAME_SIZE, m_nSampleRate);

if(associatePreprocesser)

{

speex_preprocess_ctl(m_pPreprocessorState, SPEEX_PREPROCESS_SET_ECHO_STATE,m_SpeexEchoState);

}

m_pfNoise = new float[FRAME_SIZE+1];

m_bSpeexEchoHasInit = true;

}

extern "C" __declspec(dllexport) void SpeexEchoDoAEC(short* mic, short* ref, short* out)

{

if (!m_bSpeexEchoHasInit)

{

return;

}

speex_echo_cancellation(m_SpeexEchoState,(const __int16 *) mic,(const __int16 *) ref,(__int16 *) out);

}

  编译便生成Speex.dll。

  如果对VC不熟悉也没关系,文末会直接给出libspeex.dll和Speex.dll的下载,直接使用就OK了。

  现在,C#可以调用Speex.dll导出的简单函数了,最终封装的源码如下:

/// <summary>

/// 对Speex的C#封装。

/// zhuweisky 2010.05.13

/// </summary>

public class Speex :IAudioCodec

{

private const int FrameSize = 160;

#region IsDisposed

private volatile bool isDisposed = false;

public bool IsDisposed

{

get { return isDisposed; }

}

#endregion

#region Ctor

/// <summary>

/// 初始化。

/// </summary>

/// <param name="quality">编码质量,取值0~10</param>

public Speex(int quality)

{

if (quality < 0 || quality > 10)

{

throw new Exception("quality value must be between 0 and 10.");

}

Speex.encoder_init(quality);

Speex.decoder_init();

}

#endregion

#region Dispose

public void Dispose()

{

this.isDisposed = true;

System.Threading.Thread.Sleep(100);

Speex.decoder_dispose();

Speex.encoder_dispose();

}

#endregion

#region Encode

/// <summary>

/// 将采集到的音频数据进行编码。

/// </summary>

public byte[] Encode(byte[] data)

{

if (this.isDisposed)

{

return null;

}

if (data.Length % (FrameSize * 2) != 0)

{

throw new ArgumentException("Invalid Data Length.");

}

int nbBytes;

short[] input = new short[FrameSize];

byte[] buffer = new byte[200];

byte[] output = new byte[0];

for (int i = 0; i < data.Length / (FrameSize * 2); i++)

{

for (int j = 0; j < input.Length; j++)

{

input[j] = (short)(data[i * FrameSize * 2 + j * 2] + data[i * FrameSize * 2 + j * 2 + 1] * 0x100);

}

nbBytes = Speex.encoder_encode(input, buffer);

Array.Resize<byte>(ref output, output.Length + nbBytes + sizeof(int));

Array.Copy(buffer, 0, output, output.Length - nbBytes, nbBytes);

for (int j = 0; j < sizeof(int); j++)

{

output[output.Length - nbBytes - sizeof(int) + j] = (byte)(nbBytes % 0x100);

nbBytes /= 0x100;

}

}

return output;

}

#endregion

#region Decode

/// <summary>

/// 将编码后的数据进行解码得到原始的音频数据。

/// </summary>

public byte[] Decode(byte[] data)

{

if (this.isDisposed)

{

return null;

}

int nbBytes, index = 0;

byte[] input;

short[] buffer = new short[FrameSize];

byte[] output = new byte[0];

while (index < data.Length)

{

nbBytes = 0;

index += sizeof(int);

for (int i = 1; i <= sizeof(int); i++)

nbBytes = nbBytes * 0x100 + data[index - i];

input = new byte[nbBytes];

Array.Copy(data, index, input, 0, input.Length);

index += input.Length;

Speex.decoder_decode(nbBytes, input, buffer);

Array.Resize<byte>(ref output, output.Length + FrameSize * 2);

for (int i = 0; i < FrameSize; i++)

{

output[output.Length - FrameSize * 2 + i * 2] = (byte)(buffer[i] % 0x100);

output[output.Length - FrameSize * 2 + i * 2 + 1] = (byte)(buffer[i] / 0x100);

}

}

return output;

}

#endregion

#region Pinvoke

[DllImport("Speex.dll", EntryPoint = "encoder_init")]

internal extern static void encoder_init(int quality);

[DllImport("Speex.dll", EntryPoint = "encoder_dispose")]

internal extern static void encoder_dispose();

[DllImport("Speex.dll", EntryPoint = "encoder_encode")]

internal extern static int encoder_encode(short[] data, byte[] output);

[DllImport("Speex.dll", EntryPoint = "decoder_init")]

internal extern static void decoder_init();

[DllImport("Speex.dll", EntryPoint = "decoder_dispose")]

internal extern static void decoder_dispose();

[DllImport("Speex.dll", EntryPoint = "decoder_decode")]

internal extern static void decoder_decode(int nbBytes, byte[] data, short[] output);

#endregion

}

只有四个方法:Initialize、Encode、Decode、Dispose。方法参数的含义也非常明显。

  一般音频对话的整个流程是这样的:采集 -> 编码 -> 网络传输 -> 解码 -> 播放。

  而该封装的Speex类解决了这个过程中的音频编码和解码的问题。你可以复制该源码到你的项目,并将从http://www.speex.org下载的speex.dll放到运行目录下,就可以正常地使用SPEEX的编解码功能了。

  关于Speex更高级的功能,我正在研究中,有兴趣的朋友可以email给我一起探讨。  

Speex dll 可以到官网下载页面下载。

注:我们的研究成果已经全部集成到了OMCS中,其支持回音消除(AEC)、静音检测(VAD)、噪音抑制(DENOISE)、自动增益(AGC)等网络语音技术,有兴趣的可以了解一下。

在.NET中使用Speex -- 音频数据编解码

时间: 2024-10-09 12:25:11

在.NET中使用Speex -- 音频数据编解码的相关文章

TQ210v6开发板——工业音频端口编解码详细解析(图解)

TQ210V6开发板作为天嵌2014年隆重推出的最新210开发板,其端口设计对比以前的210开发板有一定的提升.下面,小编将为大家详细解析TQ210V6开发板的音频端口的编解码设计,还附有详细的音频接口详细解析图. TQ210v6的I2S音频电路采用Wolfson Microelectronics 音频转换芯片WM8960,WM8960是一款低功耗.高质量的立体编码解码器,专为便携式数字音频应用设计. WM8960集成了一个完整的麦克风接口和一个立体声耳机驱动器,由于不再需要单独的麦克风.扬声器

Andorid中对HTML格式数据进行解码

WebView是Android开发中常用的组件之一,用来加载网页数据,可以直接传入URL,也可以传入Html格式字符等.并且我们可以通过WebView中相关方法对加载的内容进行处理,如js交互,获取加载的网页链接地址信息等.今天在开发中遇到一个小功能,就是要用WebView来加载一个网页数据,网页中有很多商品,点某一商品又可进入到App中的商品详情页面,主要方法就是通过WebViewClient中的shouldOverrideUrlLoading(WebView view, String url

音频视频-编解码工具ffmpeg

1.打开http://www.ffmpeg.org/,点击Download 2.进入http://www.ffmpeg.org/download.html,如果是windows版本点击中间的图片,出现windows Builds,点击进入 3.进入页面后点击以下链接下载 4.假如你要将F:\work\图片\hour.mp4转换为hour.ogg文件 a.打开F:\work\图片\,选取此文件夹一空白处按下Shift+鼠标右击,在调出的命令窗口处选择"在此处打开命令窗口",打开命令行程序

Qt中gb2312/GBK的URL编解码函数

编码函数: QByteArray encodeURI(QString str) { QByteArray array; QTextCodec *codec=QTextCodec::codecForName("GBK"); QByteArray tmpArray; tmpArray = codec->fromUnicode(str); for(int i=0,size = tmpArray.length();i<size;i++){ char ch = tmpArray.at

在C语言中使用Libb64进行Base64编解码

Libb64下载地址http://sourceforge.net/projects/libb64 以下为Demo CLibb64Demo.c #include <stdio.h> #include <string.h> #include "libb64/cencode.h" #include "libb64/cdecode.h" void base64EncodeString(const char *textToEncode, char *b

(原)关于OpenSL ES播放音频数据的一个奇怪的问题

关于OpenSL ES播放音频数据的一个奇怪的问题 Author:[email protected] 最近用业余时间做了一个android平台的播放器sdk,其中视频用的opengl es,音频用的opensl es 做渲染,其中整个播放器在音视频同步的过程中,使用的视频同步到音频的方式,以音频作为主时钟. 今天在测试的过程中发现一个奇怪的问题,我音频数据的填充,使用了单独的音频线程,这其中的实现,主要参考ijk的实现代码和方式. 整个流程方式:我在音频播放的过程中设置的200ms的播放缓冲区给

视音频编解码基本术语及解释

摘要:          整理了一些基本视音频术语,用于入门和查询使用. H264: H264是视频的标准,是MPEG4-10,基于内容的高效编码方式. H.264/MPEG-4第10部分,或称AVC(AdvancedVideo Coding,高级视频编码),是一种视频压缩标准,一种被广泛使用的高精度视频的录制.压缩和发布格式.第一版标准的最终草案于 整理了一些基本视音频术语,用于入门和查询使用. H264: H264是视频的标准,是MPEG4-10,基于内容的高效编码方式. H.264/MPE

Python爬虫音频数据

一:前言 本次爬取的是喜马拉雅的热门栏目下全部电台的每个频道的信息和频道中的每个音频数据的各种信息,然后把爬取的数据保存到mongodb以备后续使用.这次数据量在70万左右.音频数据包括音频下载地址,频道信息,简介等等,非常多.昨天进行了人生中第一次面试,对方是一家人工智能大数据公司,我准备在这大二的暑假去实习,他们就要求有爬取过音频数据,所以我就来分析一下喜马拉雅的音频数据爬下来.目前我还在等待三面中,或者是通知最终面试消息. (因为能得到一定肯定,不管成功与否都很开心) 二:运行环境 IDE

学习笔记-音频编解码

在记录自己学习音频的编解码之前,先来补充一下硬件方面的知识 来源于8086指令集的一系列处理器,都称为x86处理器:8086是最早的16位x86处理器,所以软件上标i386的时候,表示这个软件是32位的,基于x86处理器的:所有32位和64位的x86处理器都能运行(除非软件上标着最低处理器限制):x86_64是处理器x86的64位的扩展,实现了从32为到64位的平滑迁移,软件上标注x64或者amd64,就表示这个软件是64位的,基于x86处理器的,只有64位的处理器才能运行. X86架构是X86