Android 平台语音通话及回音消除、噪音消除研究


一 Android平台语音通讯

正因为Android平台优越的性能、美观的界面,越来越多人使用Android手机,从而在Android平台上的
语音通话越来越多。语音通话大概流程如下:我认为一个语音通话系统至少有四个模块。分别是PCM(Pulse
Code Modulation,即 脉码编码调制)语音采集,编解码,网络传输以及语音播放。如果算上UI交互的话,
就是五个模块了。整体流程大概是:A打电话给B,A声音通过MIC被采集成PCM原始数据,然后经过编码压缩,
再通过网络(建立P2P连接)将编码后的数据传输出去;B端通过网络收到数据后进行解码处理,然后调用播
放模块,进行播放数据。如果想通话音质提供些,可以在编码前加入 噪音消除,回音消除。

二 录音、放音、编码、解码、网络发送、接收

 1、语音采集模块
 Android平台上的实现是通过AudioRecord接口来实现PCM数据的采集,这一步比较容易的。但需要注意的是
AudioRecord接口的使用方法。构造AudioRecord 实例需要参数 public AudioRecord (int audioSource, int
sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes)
比如录音代码如下:

    static final int frequency = 8000;   
    static final int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;     
    static final int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;    
    int recBufSize,playBufSize; 
    AudioRecord audioRecord;    

     recBufSize =  AudioRecord.getMinBufferSize(frequency,     
              channelConfiguration, audioEncoding);      
     audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, frequency,     
      AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, recBufSize);
2、语音播放

  当语音数据采集好了之后,接着可以实现语音播放模块。Android上实现PCM数据的播放也很简单,直接
使用AudioTrack这个接口就行了。同样需要注意该接口
的使用方法。AudioTrack的构造方式跟AudioRecord是对应的

    static final int frequency = 8000;   
    static final int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;     
    static final int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;    
    int recBufSize,playBufSize; 
    AudioTrack  audioPlayer ;

  playBufSize =  AudioTrack.getMinBufferSize(frequency,     
             channelConfiguration, audioEncoding);        
  audioPlayer = new AudioTrack(AudioManager.STREAM_MUSIC,frequency,AudioFormat.CHANNEL_OUT_MONO,
  AudioFormat.ENCODING_PCM_16BIT,playBufSize,
               AudioTrack.MODE_STREAM) ;
3、语音编解码

  采集到的PCM数据是原始的语音数据,如果我们直接进行网络传输,那是不可取的。因此,要进行打包编码。
编码我们需要第三方的库,目前我使用的库是speex(http://www.speex.org)。我看到许多SIP语音电话都
使用到了这个库进行编解码。当然也有对这个库评 价不好的说法,但我觉得作为学习还是可取的,因为speex
使用起来很方便。把speex源码下载下来,写好JNI接口,在NDK环境编译一下,即可在java环境调用。
例如下面一个接口函数

 jint 
 Java_com_audiocodec_talkdemo_AudioCodec_InitAudioEncodec( JNIEnv* env,
                                                 jobject thiz,jint sampling_rate,jint audioLevel)
  {
  if(nInitAudioCodecEncodeFlag == 1 || audioLevel < 3 || audioLevel > 8 )
  return 0 ;

int  frame_size ;

if(sampling_rate == 8000)
{
audio_Leval = 0 ;          
  capAudioLength = 160     ; 
  capAudioBitrate = 8000  ; 
}else if(sampling_rate == 16000)
{
     audio_Leval = 1 ;  
     capAudioLength = 320     ;  
     capAudioBitrate = 16000  ;  
}else if(sampling_rate == 32000)
{
     audio_Leval = 2 ; 
     capAudioLength = 640     ;  
     capAudioBitrate = 32000  ;  
   }else
    return 0 ; 

tmp_Level = audioLevel ; //设置等级 15kbit/s
speex_mode = speex_lib_get_mode(audio_Leval) ; 


enc_state = speex_encoder_init(speex_mode);
speex_encoder_ctl(enc_state,SPEEX_SET_QUALITY,&tmp_Level);

int tmp = 30 ;//丢包补偿
int nRet = speex_encoder_ctl(enc_state, SPEEX_SET_PLC_TUNING, &tmp);
nRet = speex_encoder_ctl(enc_state, SPEEX_GET_PLC_TUNING, &tmp);

speex_bits_init(&bits); 

   nInitAudioCodecEncodeFlag = 1 ;

   return 1 ;
 }

//编码音频数据
/*
参数
    jbyteArray  szAudio   等待编码的音频数据
    jbyteArray  szOut     编码后的音频数据
返回值
    成功返回 编码后长度
    失败返回 0 
*/

jint Java_com_audiocodec_talkdemo_AudioCodec_AudioEncode( JNIEnv* env,
                            jobject thiz,jbyteArray szAudio,jbyteArray szOut)
{
if(nInitAudioCodecEncodeFlag == 0)
  return 0 ;

  jbyte* szAudioBuffer =  (jbyte *)(*env)->GetByteArrayElements(env,szAudio, 0);
  jbyte* szOutBuffer   =  (jbyte *)(*env)->GetByteArrayElements(env,szOut, 0);

//清空bits ,以便编码
  speex_bits_reset(&bits);

    //进行编码
  int nRet = speex_encode_int(enc_state,(spx_int16_t*)szAudioBuffer, &bits);

  //把编码后的bits 结构,拷贝到cbits_enc的数据可以从网络发送出去,长度为nByte_enc
  int nByte_enc = speex_bits_write(&bits, szOutBuffer, 200);

  (*env)->ReleaseByteArrayElements(env,szAudio,szAudioBuffer,0) ;
  (*env)->ReleaseByteArrayElements(env,szOut,szOutBuffer,0) ;

  return nByte_enc ;
}                                                 

/*
函数功能 初始化编码器
参数
    无参数
返回值
    成功返回 1
    失败返回 0 
*/
jint 
Java_com_audiocodec_talkdemo_AudioCodec_ExitAudioEncodec( JNIEnv* env,
                                                 jobject thiz)
{
if(nInitAudioCodecEncodeFlag == 1)
{
nInitAudioCodecEncodeFlag = 0 ;

//销毁资源
speex_bits_destroy(&bits); 
speex_encoder_destroy(enc_state);
enc_state = NULL ;
}else
return 0 ;
}
4 网络发送、接收
   //定义
DatagramSocket udpSocket  ;  

//生成
      try {
udpSocket = new  DatagramSocket(6789);
  } catch (SocketException e1) {
   e1.printStackTrace();
 }

 //发送
   try {
udpSocket.send(sendPacket) ;
 } catch (IOException e) {
e.printStackTrace();
 }

  //接收
  udpSocket.receive(udpPackage); 

  //关闭
  udpSocket.close() ;

三、 回音消除

    从Speex 的介绍可以看出它提供了噪音消除,回音消除,测试比较过噪音消除这功能效果是非
 常棒的,回音消除这功能也很不错这一功能,现在开源的,比较完善的回音消除模块就是Speex了
 ,有许多中小公司也拿它作为回音消除功能 。经过测试,Speex的消除效果还是不错的。
 编写个jni文件,NDK 环境编译一下即可得到so 文件,在Android环境中调用即可。
      //初始化回音消除参数
      /*
       * jint frame_size        帧长      一般都是  80,160,320
       * jint filter_length     尾长      一般都是  80*25 ,160*25 ,320*25
       * jint sampling_rate     采样频率  一般都是  8000,16000,32000
       * 比如初始化 
       *  InitAudioAEC(80, 80*25,8000)   //8K,10毫秒采样一次
       *  InitAudioAEC(160,160*25,16000) //16K,10毫秒采样一次
       *  InitAudioAEC(320,320*25,32000) //32K,10毫秒采样一次
       */
jint Java_com_audioaec_talkdemo_AudioAEC_InitAudioAEC( JNIEnv* env,jobject thiz,
              jint frame_size,jint filter_length,jint sampling_rate)
{
if(nInitSuccessFlag == 1)
return 1 ;

m_nFrameSize  = frame_size; 
m_nFilterLen  = filter_length; 
m_nSampleRate = sampling_rate; 

//计算采样时长,即是10毫秒,还是20毫秒,还是30毫秒
nSampleTimeLong = (frame_size / (sampling_rate / 100)) * 10 ;

m_pState = speex_echo_state_init(m_nFrameSize, m_nFilterLen); 
if(m_pState == NULL)
return -1 ;

m_pPreprocessorState = speex_preprocess_state_init(m_nFrameSize, m_nSampleRate); 
if(m_pPreprocessorState == NULL)
return -2 ;

iArg = m_nSampleRate;
speex_echo_ctl(m_pState, SPEEX_SET_SAMPLING_RATE, &iArg);
speex_preprocess_ctl(m_pPreprocessorState, SPEEX_PREPROCESS_SET_ECHO_STATE, m_pState);

 nInitSuccessFlag = 1 ;

 return 1 ;
}

/*
 参数:
  jbyteArray recordArray  录音数据
  jbyteArray playArray    放音数据 
  jbyteArray szOutArray
*/
jint Java_com_audioaec_talkdemo_AudioAEC_AudioAECProc(JNIEnv* env,jobject thiz,
           jbyteArray recordArray,jbyteArray playArray,jbyteArray szOutArray  )
{
if(nInitSuccessFlag == 0)
return 0 ;

 jbyte* recordBuffer = (jbyte *)(*env)->GetByteArrayElements(env,recordArray, 0);
 jbyte* playBuffer = (jbyte *)(*env)->GetByteArrayElements(env,playArray, 0);
 jbyte* szOutBuffer = (jbyte *)(*env)->GetByteArrayElements(env,szOutArray, 0);

speex_echo_cancellation(m_pState,(spx_int16_t *)recordBuffer,
       (spx_int16_t *)playBuffer,(spx_int16_t *)szOutBuffer); 
int flag=speex_preprocess_run(m_pPreprocessorState,(spx_int16_t *)szOutBuffer);  

 (*env)->ReleaseByteArrayElements(env,recordArray,recordBuffer,0) ;
 (*env)->ReleaseByteArrayElements(env,playArray,playBuffer,0) ;
 (*env)->ReleaseByteArrayElements(env,szOutArray,szOutBuffer,0) ;

 return 1 ;
}

    //退出
jint Java_com_sosea_xmeeting_SpeexAEC_ExitSpeexDsp( JNIEnv* env,jobject thiz)
{
if(nInitSuccessFlag == 0)
return 0 ;

if (m_pState != NULL) 
{ 
speex_echo_state_destroy(m_pState); 
m_pState = NULL; 
} 
if (m_pPreprocessorState != NULL) 
{ 
speex_preprocess_state_destroy(m_pPreprocessorState); 
m_pPreprocessorState = NULL; 
} 

 nInitSuccessFlag = 0 ;

 return 1 ;
}

四 、 噪音消除处理

// 初始化 降噪
Java_com_audioaec_talkdemo_AudioAEC_InitAudioDeNose( JNIEnv* env,
                                                 jobject thiz)
{
 int denoise_enabled = 1 ;
if(nInitDeNoseFlag == 1)
return 0 ;

 nInitDeNoseFlag = 1 ;

 //8K降噪
audioProcNose8K = speex_preprocess_state_init(80 * (nSampleTimeLong / 10),8000);  
speex_preprocess_ctl(audioProcNose8K, SPEEX_PREPROCESS_SET_DENOISE, &denoise_enabled);

//16K降噪
audioProcNose16K = speex_preprocess_state_init(160 * (nSampleTimeLong / 10),16000);  
speex_preprocess_ctl(audioProcNose16K, SPEEX_PREPROCESS_SET_DENOISE, &denoise_enabled);

 return 1 ;
}

//8K降噪 
jint Java_com_audioaec_talkdemo_AudioAEC_AudioDeNose8K(JNIEnv* env,jobject thiz,jbyteArray recordArray)
{
if(nInitDeNoseFlag == 0)
return 0 ;

  jbyte* recordBuffer = (jbyte *)(*env)->GetByteArrayElements(env,recordArray, 0);

  speex_preprocess(audioProcNose8K,(spx_int16_t*)recordBuffer, NULL);

 (*env)->ReleaseByteArrayElements(env,recordArray,recordBuffer,0) ;

 return 1 ;
}

//16K降噪 
jint Java_com_audioaec_talkdemo_AudioAEC_AudioDeNose16K(JNIEnv* env,jobject thiz,jbyteArray recordArray)
{
if(nInitDeNoseFlag == 0)
return 0 ;

  jbyte* recordBuffer = (jbyte *)(*env)->GetByteArrayElements(env,recordArray, 0);

  speex_preprocess(audioProcNose16K,(spx_int16_t*)recordBuffer, NULL);

 (*env)->ReleaseByteArrayElements(env,recordArray,recordBuffer,0) ;

 return 1 ;
}

// 释放降噪
jint 
Java_com_audioaec_talkdemo_AudioAEC_ExitAudioDeNose( JNIEnv* env,
                                                 jobject thiz)
{
if(nInitDeNoseFlag == 0)
return 0 ;

 nInitDeNoseFlag = 0 ;

speex_preprocess_state_destroy(audioProcNose8K); 
speex_preprocess_state_destroy(audioProcNose16K); 

 return 1 ;
}
### 回答1: Android的麦克风降噪是指通过软件算法对麦克风采集到的声音进行去除噪声的处理。 在Android系统中,麦克风降噪功能通常由系统自带的音频处理库来实现。当用户使用麦克风进行录音时,系统会对麦克风采集到的声音进行处理,通过降低环境背景噪声的干扰,使得录音更加清晰。 麦克风降噪的原理一般包括以下几个步骤:首先,麦克风采集到的声音会被分析,识别出其中的噪声和语音信号;然后,针对噪声信号,系统会根据预先训练好的模型进行降噪处理,通过滤波等方法将噪声信号减弱;最后,经过降噪处理后的信号会与原始声音进行合成,得到最终的清晰语音。 需要注意的是,Android的麦克风降噪功能通常需要具备较高的计算能力和算法支持,因此并非所有的Android设备都能够提供相同水平的降噪效果。一些高端Android设备可能会配备专门的硬件降噪芯片,能够在硬件层面上提供更出色的降噪效果。 总之,Android的麦克风降噪是通过软硬件配合实现的,能够有效地降低环境噪声对录音的影响,提供更好的语音录入体验。 ### 回答2: Android 麦克风降噪是一种通过软件和硬件技术来减少环境噪音对麦克风录音质量的影响的功能。在录制音频时,环境噪音常常会导致录音结果的质量下降,影响语音识别、通话质量等应用。为了解决这个问题,Android系统为麦克风降噪提供了以下技术: 首先,硬件方面,安卓设备通常会采用双麦克风或多麦克风阵列。通过将多个麦克风放置在不同位置,可以获取到不同的声音输入,并在后续处理过程中利用这些输入进行降噪处理。 其次,软件方面,Android系统提供了多种降噪算法和技术。其中最常用的算法是频域滤波算法和时域滤波算法。频域滤波算法通过将录音信号转换到频域,在频域上对噪音进行识别和去除;时域滤波算法则是在时间域上对录音信号进行处理,通过识别和衰减噪音信号。 此外,还有一些降噪技术,如自适应滤波和声学回音消除。自适应滤波根据麦克风输入信号的统计特征来调整滤波器的参数,从而提高降噪效果。声学回音消除是通过分析录音输入和扬声器输出之间的关系,削弱或消除回声录音质量的影响。 综上所述,Android麦克风降噪通过硬件和软件技术的结合,能够有效降低环境噪音录音质量的干扰,提升音频采集的清晰度和准确性,进而提升语音识别、通话质量等应用的用户体验。 ### 回答3: Android麦克风降噪是指在Android设备上通过软件或硬件技术进行的一种降低麦克风录音噪音处理。由于手机或平板电脑等设备的麦克风通常位于外部环境中,容易受到周围噪音的干扰,因此降噪技术在提高音频质量和语音识别准确性方面起到了重要作用。 Android平台上的麦克风降噪技术主要有以下几种方式: 1. 软件降噪:在录音软件中,通过软件算法对录音进行实时降噪处理。这种方式可以通过降低录音信号中的噪音成分,提高录音的信噪比,从而增强录音的清晰度和可听性。 2. 数字信号处理(DSP):部分Android设备配备了专用的数字信号处理芯片,该芯片能够实时对录音信号进行数字降噪处理。DSP技术能够智能地识别并过滤掉环境噪音,保留重要的语音信号,可以有效提高录音质量。 3. 麦克风的物理设计:一些Android设备在硬件层面通过改善麦克风的物理设计,来减少环境噪音的干扰。例如,采用双麦克风的设计可以通过麦克风阵列获取不同角度的信号从而降低噪音。 需要注意的是,对于不同的Android设备,降噪技术的实现方式和效果可能会有所不同。而且,全面的麦克风降噪效果除了依赖于设备本身的技术水平外,也取决于录音环境的实际情况。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值