speex 알고리즘 안 드 로 이 드 이식

l 최근 에 speex 인터페이스 파 라 메 터 를 조정 하고 speex 알고리즘 의 일부 특성 을 추가 합 니 다. 예 를 들 어 소음 감소, 음소 거 검 측, 백색 소음 추가, 이득 등 입 니 다.다음은 스 펙 스 계산 을 간단하게 소개 하 겠 습 니 다.
법.speex 음성 알고리즘 은 주로 VOIP 응용 을 위 한 오픈 소스 알고리즘 으로 그 는 여러 가지 기능 을 모 았 다. 상기 와 같이 메아리 제거 (ACE) 등 기능 도 추가 하여 다양한 플랫폼 에서
응용 하 다.다음은 speex 가 안 드 로 이 드 플랫폼 에서 의 응용 을 소개 합 니 다.
우선 speex 알고리즘 의 모듈 구분 을 소개 합 니 다.소개 하기 전에 speex 홈 페이지 에 가 는 것 이 좋 습 니 다.  http://www.speex.org/downloads/ 관련 문서 와 원본 을 다운로드 하 세 요.그것
가방 speex - api - reference. tar. gz 에 speex 모듈 에 대한 소개 가 있 습 니 다.그 API 소 개 는 1.2 까지 speex 는 모두 다음 과 같은 9 대 모듈 로 나 뉜 다.
--Speex encoder and decoder。——인 코딩 과 디 코딩 모듈.
--SpeexBits:Bit-stream mainpulations。——비트 흐름 조작 모듈, 즉 데이터 의 읽 기와 쓰기 모듈 입 니 다.
--Various definitions for Speex callbacks supported by the decoder。——디 코딩 리 셋 모듈.
--SpeexEchoState:Acoustic echo caceller。——메아리 제거 모듈.
--SpeexHeader:Makes it easy to writ/parse an Ogg/Speex header。——ogg 형식 관련 처리 모듈.
--JitterBuffer:Adaptive jitter buffer。——음성 디 더 링 버퍼 모듈.
--SpeexJitter:Adaptive jitter buffer specifically for Speex。——speex 알고리즘 특징 에 최 적 화 된 언어 디 더 링 처리 모듈.
--SpeexPreprocessState:The Speex preprocessor。——Speex 기타 관련 특징의 처리 모듈, 예 를 들 어 소음 감소, 음소 거 검사 등.
--SpeexStereoState:Handing Speex stereo files。——스테레오 처리 관련 모듈.
이상 은 Speex 알고리즘 의 주요 모듈 입 니 다. 각 모듈 은 관련 기능 의 함수 인터페이스 가 있 습 니 다. 구체 적 으로 우 리 는 api 에 관 한 소 개 를 볼 수 있 습 니 다.
자, 이제 안 드 로 이 드 플랫폼 에서 의 사용 을 소개 하 겠 습 니 다.C 를 사용 하기 때문에 안 드 로 이 드 에서 호출 하려 면 JNI 를 통 해 호출 해 야 하기 때문에
우 리 는 먼저 speex 알고리즘 의. so 파일 을 얻어 야 하기 때문에 cygwin 컴 파일 을 사용 하여. so 파일 을 가 져 옵 니 다.
1. speex 관련 소스 코드 를 프로젝트 에 복사 합 니 다.
speex 소스 코드 를 다운로드 하고 프로젝트 에 새 폴 더 를 만 들 고 jni 라 고 명명 합 니 다.speex 원본 코드 의 include, libspeex 두 파일 의 원본 코드 를 jni 폴 더 에 복사 합 니 다.include 폴 더 의
speex_config_types. h. in 파일 을 speex 로 변경config_types. h 파일 을 사용 하고 그 내용 을 다음 과 같이 변경 합 니 다.
#ifndef _SPEEX_CONFIG_TYPES_H
#define _SPEEX_CONFIG_TYPES_H

   typedef signed short spx_int16_t;
   typedef unsigned short spx_uint16_t;
   typedef signed int spx_int32_t;
   typedef unsigned int spx_uint32_t;

#endif  /* _SPEEX_CONFIG_TYPES_H */

2. Android. mk 및 application. mk 관련 파일 을 작성 합 니 다.
Android. mk 파일 내용 은 다음 과 같 습 니 다.
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := libspeex
LOCAL_CFLAGS = -DFIXED_POINT -DUSE_KISS_FFT -DEXPORT="" -UHAVE_CONFIG_H
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include

LOCAL_SRC_FILES := speex_jni.cpp \
				./libspeex/bits.c \
				./libspeex/buffer.c \
				./libspeex/cb_search.c \
				./libspeex/exc_10_16_table.c \
				./libspeex/exc_10_32_table.c \
				./libspeex/exc_20_32_table.c \
				./libspeex/exc_5_256_table.c \
				./libspeex/exc_5_64_table.c \
				./libspeex/exc_8_128_table.c \
				./libspeex/fftwrap.c \
				./libspeex/filterbank.c \
				./libspeex/filters.c \
				./libspeex/gain_table.c \
				./libspeex/gain_table_lbr.c \
				./libspeex/hexc_10_32_table.c \
				./libspeex/hexc_table.c \
				./libspeex/high_lsp_tables.c \
				./libspeex/jitter.c \
				./libspeex/kiss_fft.c \
				./libspeex/kiss_fftr.c \
				./libspeex/lpc.c \
				./libspeex/lsp.c \
				./libspeex/lsp_tables_nb.c \
				./libspeex/ltp.c \
				./libspeex/mdf.c \
				./libspeex/modes.c \
				./libspeex/modes_wb.c \
				./libspeex/nb_celp.c \
				./libspeex/preprocess.c \
				./libspeex/quant_lsp.c \
				./libspeex/resample.c \
				./libspeex/sb_celp.c \
				./libspeex/scal.c \
				./libspeex/smallft.c \
				./libspeex/speex.c \
				./libspeex/speex_callbacks.c \
				./libspeex/speex_header.c \
				./libspeex/stereo.c \
				./libspeex/vbr.c \
				./libspeex/vq.c \
				./libspeex/window.c

include $(BUILD_SHARED_LIBRARY)

구체 적 인 각 줄 의 의 미 는 제 이전의 블 로 그 를 참조 하거나 Android. mk 의 작성 방법 을 스스로 검색 할 수 있 습 니 다.
Applicatio. mk 내용 은 다음 과 같 습 니 다.
APP_ABI := armeabi armeabi-v7a

3. 로 컬 방법 인터페이스 파일 speex 작성
	public native int open(int compression);
	public native int getFrameSize();
	public native int decode(byte encoded[], short lin[], int size);
	public native int encode(short lin[], int offset, byte encoded[], int size);
	public native void close();

4. 자바 의 자바 도 구 를 사용 하여 이 jni 인터페이스 파일 을 컴 파일 합 니 다.
cmd 를 사용 하여 프로젝트 빈 / classes 디 렉 터 리 에 들 어가 다음 명령 을 입력 하 십시오: javah - jni xxx. xxx. xxx. speex.앞의 xxx 는 speex 파일 의 가방 이름 입 니 다.컴 파일 이 완료 되면 classes 파일 에서 볼 수 있 습 니 다.
하나의 compoctalk_codec_Speex. h 파일, 이 파일 을 jni 디 렉 터 리 에 복사 합 니 다.
5. speex. cpp 파일 작성
#include <jni.h>
#include "com_poctalk_codec_Speex.h"
#include <string.h>
#include <unistd.h>
#include <speex/speex.h>
#include <speex/speex_preprocess.h>
#include <speex/speex_echo.h>
#pragma comment(lib,"libspeexdsp.lib")

static int codec_open = 0;
static int dec_frame_size;
static int enc_frame_size;

static SpeexBits ebits, dbits;
void *enc_state;
void *dec_state;
SpeexPreprocessState *preprocess_state;
//SpeexEchoState *echo_state;
static JavaVM *gJavaVM;

extern "C"{
	 JNIEXPORT jint JNICALL Java_com_poctalk_codec_Speex_open(JNIEnv *env, jobject obj, jint compression) {
		int tmp;

		if (codec_open++ != 0)
			return (jint)0;

		speex_bits_init(&ebits);
		speex_bits_init(&dbits);
		//         
		enc_state = speex_encoder_init(&speex_nb_mode);
		dec_state = speex_decoder_init(&speex_nb_mode);
		//         
		//enc_state = speex_encoder_init(&speex_wb_mode);
		//dec_state = speex_decoder_init(&speex_wb_mode);
		tmp = compression;
		speex_encoder_ctl(enc_state, SPEEX_SET_QUALITY, &tmp);//        ,     。   tmp  
		speex_encoder_ctl(enc_state, SPEEX_GET_FRAME_SIZE, &enc_frame_size);
		speex_decoder_ctl(dec_state, SPEEX_GET_FRAME_SIZE, &dec_frame_size);

		preprocess_state =speex_preprocess_state_init(160, 8000);//       

		//echo_state = speex_echo_state_init(160, 5000);//        
		//int sampleRate = 8000;
		//speex_echo_ctl(echo_state, SPEEX_ECHO_SET_SAMPLING_RATE, &sampleRate);

		int denoise = 1;
		int noiseSuppress = -25;
		speex_preprocess_ctl(preprocess_state, SPEEX_PREPROCESS_SET_DENOISE, &denoise); //  
		speex_preprocess_ctl(preprocess_state, SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, &noiseSuppress); //     dB


		int agc = 1;
		float q=24000;
		//actually default is 8000(0,32768),here make it louder for voice is not loudy enough by default. 8000
		speex_preprocess_ctl(preprocess_state, SPEEX_PREPROCESS_SET_AGC, &agc);//  
		speex_preprocess_ctl(preprocess_state, SPEEX_PREPROCESS_SET_AGC_LEVEL,&q);

		int vad = 1;
		int vadProbStart = 80;
		int vadProbContinue = 65;
		speex_preprocess_ctl(preprocess_state, SPEEX_PREPROCESS_SET_VAD, &vad); //    
		speex_preprocess_ctl(preprocess_state, SPEEX_PREPROCESS_SET_PROB_START , &vadProbStart); //Set probability required for the VAD to go from silence to voice
		speex_preprocess_ctl(preprocess_state, SPEEX_PREPROCESS_SET_PROB_CONTINUE, &vadProbContinue); //Set probability required for the VAD to stay in the voice state (integer percent)

		return (jint)0;
	 }
	
	 JNIEXPORT jint JNICALL Java_com_poctalk_codec_Speex_encode
		(JNIEnv *env, jobject obj, jshortArray lin, jint offset, jbyteArray encoded, jint size) {

		jshort buffer[enc_frame_size];
		jbyte output_buffer[enc_frame_size];
		int nsamples = (size-1)/enc_frame_size + 1;
		int i, tot_bytes = 0;

		if (!codec_open)
			return 0;

		speex_bits_reset(&ebits);//                 

		speex_echo_state_reset(echo_state);//

		for (i = 0; i < nsamples; i++) {
			env->GetShortArrayRegion(lin, offset + i*enc_frame_size, enc_frame_size, buffer);

			//input_frame         ,Echo_Data  speaker       ,out_frame         
			//speex_echo_cancellation(echo_state,input_frame,Echo_Data,out_frame);//    

			speex_preprocess_run(preprocess_state, buffer);
			speex_encode_int(enc_state, buffer, &ebits);
		}

		tot_bytes = speex_bits_write(&ebits, (char *)output_buffer,enc_frame_size);//           
		env->SetByteArrayRegion(encoded, 0, tot_bytes,output_buffer);

		return (jint)tot_bytes;
	 }

	 JNIEXPORT jint JNICALL Java_com_poctalk_codec_Speex_decode
		(JNIEnv *env, jobject obj, jbyteArray encoded, jshortArray lin, jint size) {

			jbyte buffer[dec_frame_size];
			jshort output_buffer[dec_frame_size];
			jsize encoded_length = size;

		if (!codec_open)
			return 0;

		env->GetByteArrayRegion(encoded, 0, encoded_length, buffer);
		speex_bits_read_from(&dbits, (char *)buffer, encoded_length);
		speex_decode_int(dec_state, &dbits, output_buffer);
		env->SetShortArrayRegion(lin, 0, dec_frame_size,
					 output_buffer);

		return (jint)dec_frame_size;
	 }

	 JNIEXPORT jint JNICALL Java_com_poctalk_codec_Speex_getFrameSize(JNIEnv *env, jobject obj) {

		if (!codec_open)
			return 0;
		return (jint)enc_frame_size;
	 }

	 JNIEXPORT void JNICALL Java_com_poctalk_codec_Speex_close(JNIEnv *env, jobject obj) {

		if (--codec_open != 0){
			return;
		}
		//speex_echo_state_destroy(echo_state);//

		speex_preprocess_state_destroy(preprocess_state);
		speex_bits_destroy(&ebits);
		speex_bits_destroy(&dbits);

		speex_decoder_destroy(dec_state);
		speex_encoder_destroy(enc_state);
	 }
}

6. cygwin 을 사용 하여 전체 항목 을 컴 파일 합 니 다.
컴 파일 이 완료 되면 refresh 프로젝트 는 libs 디 렉 터 리 에서 두 개의 폴 더 armeabi 를 생 성 합 니 다. armeabi - v7a 에는 각각 libspeex. so 파일 이 있 습 니 다.
이로써. so 파일 의 컴 파일 이 완료 되 었 습 니 다. 프로젝트 에서 로 컬 방법 을 호출 하여 음성 디 코딩 을 할 수 있 습 니 다.사용 에 있어 서 나 는 이미 프로젝트 에서 응용 을 했 기 때문에 그렇지 않다.
끊 었 습 니 다. 하지만 제 언어 모듈 도 인터넷 의 한 항목 을 참고 하여 작 성 했 습 니 다. 이름 은 안 드 로 이 드 - recorder - 6.0 입 니 다. 그의 소스 코드 를 다운로드 하여 모방 할 수 있 습 니 다.근 데 또 필요 한 게 있어 요.
설명 하 는 것 은. cpp 파일 을 작성 할 때 저 는 메아리 제거 기능 을 추가 하지 않 았 습 니 다. 메아리 제거 문제 에 많은 시간 을 낭 비 했 습 니 다. 처음에 그의 api 를 보지 않 았 는데 메아리 가 없어 질 줄 몰 랐 습 니 다.
어떤 모듈 이 이 루어 졌 는 지 메아리 제거 api 를 어떻게 사용 해 야 할 지 몰 랐 습 니 다. 나중에 api 를 보 았 는데 메아리 제거 함 수 를 호출 할 때 파 라 메 터 를 어떻게 전달 해 야 할 지 몰 랐 습 니 다. 나중에 동료 에 게 물 었 습 니 다.
메아리 제거 기능 은 전 양 방향 통신 방식, 즉 스피커 와 녹음 모듈 을 모두 켜 는 것 이다. 만약 에 반 양 방향 통신 방식 이 라면 예 를 들 어 핸드폰, 메아리 제거 기능 은 있 으 나 마 나 하 다.
하지만 언급 하고 또 많은 시간 을 낭비 했다 면 메아리 제거 기능 의 호출 에 대해 이야기 해 보 자.
우선 우리 가 예비 처 리 를 할 때 메아리 가 제거 해 야 할 예비 처리:
		//echo_state = speex_echo_state_init(160, 5000);//        
		//int sampleRate = 8000;
		//speex_echo_ctl(echo_state, SPEEX_ECHO_SET_SAMPLING_RATE, &sampleRate);

sample Rate 는 바로 우리 가 설정 한 녹음 사용 빈도 이다.
그리고 음성 인 코딩 을 할 때 메아리 제거 기능 을 호출 합 니 다.
			//input_frame         ,Echo_Data  speaker       ,out_frame         
			//speex_echo_cancellation(echo_state,input_frame,Echo_Data,out_frame);//    

위 speexecho_cancellation 함수 의 세 가지 매개 변 수 는 메아리 제거 대상, inpt 입 니 다.frame 스피커 재생 데이터, EchoData 마이크 에서 얻 은 데이터, outframe 마지막
메아리 제거 후의 데이터.이 몇 개의 매개 변수 에 대해 비교적 현혹 되 는 사람 이 있 을 수 있다. 그것 은 메아리 가 발생 하 는 원인 을 모 르 기 때문이다.전 양 방향 통신 이기 때문에, 우리 가 녹음 할 때 도 소 리 를 내 고 있 을 수 있다.
재생 하면 가끔 녹음 도 스피커 가 재생 하고 있 는 소 리 를 녹음 하기 때문에 메아리 효과 가 발생 하기 때문에 두 번 째 매개 변 수 는 재생 된 데 이 터 를 매개 변수 로 전달 해 야 한다.
. so 파일 을 컴 파일 하 는 과정 에서 저 는 이런 문제 도 만 났 습 니 다. multiple definition.나중에 야 내 android. mk 파일 에서 speexjni. cpp 는 두 번 인용 했다.
이 블 로 그 를 찾 은 후에 야 나의 이 문 제 를 발견 했다.http://www.cnblogs.com/hewei2012/p/3370299.html 이 문제 에 있어 서 나의 시간 을 가장 오래 써 서 부주의 로 사람 을 죽 였 다.
참고 블 로그:
http://www.cppblog.com/tx7do/archive/2012/11/23/195607.html
http://www.cnblogs.com/stay/archive/2011/09/06/2168751.html
http://www.cnblogs.com/chef/archive/2012/07/19/2599067.html
http://www.cnblogs.com/rosesmall/archive/2012/04/18/2455395.html
http://www.cnblogs.com/myitm/archive/2011/07/21/2113299.html
http://www.cnblogs.com/kinyer/p/3326570.html
http://www.cnblogs.com/ldjrl2013/p/3687938.html
http://blog.csdn.net/zblue78/article/details/5841357

좋은 웹페이지 즐겨찾기