speex编解码
Speex是一套主要针对语音的开源免费,无专利保护的音频压缩格式。Speex工程着力于通过提供一个可以替代高性能语音编解码来降低语音应用输入门槛。另外,相对于其它编解码,Speex也很适合网络应用,在网络应用上有着自己独特的优势。同时,Speex还是GNU工程的一部分,在改版的BSD协议中得到了很好的支持。Speex是基于CELP并且专门为码率在2-44kbps的语音压缩而设计的。Speex源码是基于c语音实现的(也有java实现,效率相对较低)。
1、去Speex官网下载最新Speex源码。
2、创建新的android工程,并创建jni文件夹。
3、把speex源码目录下的libspeex和include目录及其子目录文件全部拷贝到$project/jni目录下。
4、在jni目录下新增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 := 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 speex_jni.cpp include $(BUILD_SHARED_LIBRARY)
5、在jni目录下新增Application.mk文件,编辑内容如下
APP_ABI := armeabi armeabi-v7a
6、在$project/jni/include/speex/目录下新增speex_config_types.h文件,编辑内容如下
#ifndef __SPEEX_TYPES_H__ #define __SPEEX_TYPES_H__ typedef short spx_int16_t; typedef unsigned short spx_uint16_t; typedef int spx_int32_t; typedef unsigned int spx_uint32_t; #endif
7、创建JNI包装类speex_jni.cpp,用来调用Speex中的C代码函数,编辑内容如下
#include <jni.h> #include <string.h> #include <unistd.h> #include <speex/speex.h> 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; static JavaVM *gJavaVM; extern "C" JNIEXPORT jint JNICALL Java_com_trunkbow_speextest_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); tmp = compression; speex_encoder_ctl(enc_state, SPEEX_SET_QUALITY, &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); return (jint)0; } extern "C" JNIEXPORT jint Java_com_trunkbow_speextest_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); for (i = 0; i < nsamples; i++) { env->GetShortArrayRegion(lin, offset + i*enc_frame_size, enc_frame_size, buffer); speex_encode_int(enc_state, buffer, &ebits); } //env->GetShortArrayRegion(lin, offset, enc_frame_size, 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; } extern "C" JNIEXPORT jint JNICALL Java_com_trunkbow_speextest_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; } extern "C" JNIEXPORT jint JNICALL Java_com_trunkbow_speextest_Speex_getFrameSize (JNIEnv *env, jobject obj) { if (!codec_open) return 0; return (jint)enc_frame_size; } extern "C" JNIEXPORT void JNICALL Java_com_trunkbow_speextest_Speex_close (JNIEnv *env, jobject obj) { if (--codec_open != 0) return; speex_bits_destroy(&ebits); speex_bits_destroy(&dbits); speex_decoder_destroy(dec_state); speex_encoder_destroy(enc_state); }
8、在Java层创建Speex工具类,内容如下:
package com.trunkbow.speextest; public class Speex { /* quality * 1 : 4kbps (very noticeable artifacts, usually intelligible) * 2 : 6kbps (very noticeable artifacts, good intelligibility) * 4 : 8kbps (noticeable artifacts sometimes) * 6 : 11kpbs (artifacts usually only noticeable with headphones) * 8 : 15kbps (artifacts not usually noticeable) */ private static final int DEFAULT_COMPRESSION = 8; Speex() { } public void init() { load(); open(DEFAULT_COMPRESSION); } private void load() { try { System.loadLibrary("speex"); } catch (Throwable e) { e.printStackTrace(); } } 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(); }
9、使用cygwin编译,生成so文件。
ilbc编解码
iLBC是为专为提供稳健的IP语音通信而开发的语音codec,以窄带语音为设计基础,具有8kHz的采样率。iLBC codec支持两种基本的帧长度:13.3 kbps比特率下编码帧长度为30ms;而15.2 kbps比特率下编码帧长度则为20ms。
采用iLBC算法可以获得一个具有丢包响应控制的语音编码系统。iLBC对每一个数据包的处理都能够独立于其它数据包来进行,是数据包通信的理想选择。即使IP丢包和/或延迟现象的恶化,这种codec的语音质量下降情况也不会太差。这与基于CEIP模型的一般codec的行为不同,这类codec最先是为交换电路网络或无线网络而设计的,是设计来恢复位错误而非丢包的。
丢包现象发生时,语音codec的一项相关基准是从单个丢包情况下恢复过来所需的帧/包数量。在iLBC的情况中,数量是零。在丢包之后的第一个数据包总仍能按原本安排的被精确解码。
iLBC是一种窄带语音codec,使用了整个4kHz频带,而大多数标准低比特率codec只利用从300Hz到3400Hz的频带。这一点对音质的影响是相当明显的。此外,iLBC语音编码的频谱特性精确模拟了原始信号的特性,其语音比标准低比特率codec的更自然清晰。
总而言之,iLBC算法为数据包网络实现了尖端的固定比特率编码,在质量与比特率之间取得了非常出色的平衡。
iLBC算法也是开源算法,在GitHub可以下载到,源地址如下:
https://github.com/bjdodson/iLBC-Android
同时code.google(http://code.google.com/p/android-ilbc/)上也有一份,code.google上的比较清晰,这一篇主要参考了android-ilbc。
git下android-ilbc工程,导入到eclipse中,jni部分使用cygwin编译。git对有些开发者还不方便,我在csdn的资源模块。
上传了一份编译好的,地址如下:http://download.csdn.net/detail/xyz_lmn/4594662
Silk编解码
Silk编解码是Skype向第三方开发人员和硬件制造商提供免版税认证(RF)的Silk宽带音频编码器。Skype已将其开源,可以访问http://developer.skype.com/silk获取最新动向。SILK Codec是一个语音和音频编解码算法,对于音频带宽、网络带宽和算法复杂度都具有很好的弹性。支持4种采样率:8KHz、12KHz、16KHz、24KHz;三种复杂度:低、中、高。编码码率在6~40kbps(不同采样率具有不同的码率范围)以及还支持VAD、DTX、FEC等模块,感觉还是比较全面。最重要的一点是提供了定点C代码,非常有利于向ARM、DSP移植和优化。这一篇主要参考了pjsip中的silk实现。
1、获取silk源码(http://developer.skype.com/silk)
2、创建新的android工程,并创建jni文件夹。
3、将silk源码拷贝到jni目录
4、在jni目录下新增Android.mk文件,编辑内容如下:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) SILK := silk LOCAL_MODULE := silkcommon LOCAL_SRC_FILES := $(SILK)/src/SKP_Silk_A2NLSF.c $(SILK)/src/SKP_Silk_CNG.c $(SILK)/src/SKP_Silk_HP_variable_cutoff_FIX.c $(SILK)/src/SKP_Silk_LBRR_reset.c $(SILK)/src/SKP_Silk_LPC_inv_pred_gain.c $(SILK)/src/SKP_Silk_LPC_stabilize.c $(SILK)/src/SKP_Silk_LPC_synthesis_filter.c $(SILK)/src/SKP_Silk_LPC_synthesis_order16.c $(SILK)/src/SKP_Silk_LP_variable_cutoff.c $(SILK)/src/SKP_Silk_LSF_cos_table.c $(SILK)/src/SKP_Silk_LTP_analysis_filter_FIX.c $(SILK)/src/SKP_Silk_LTP_scale_ctrl_FIX.c $(SILK)/src/SKP_Silk_MA.c $(SILK)/src/SKP_Silk_NLSF2A.c $(SILK)/src/SKP_Silk_NLSF2A_stable.c $(SILK)/src/SKP_Silk_NLSF_MSVQ_decode.c $(SILK)/src/SKP_Silk_NLSF_MSVQ_encode_FIX.c $(SILK)/src/SKP_Silk_NLSF_VQ_rate_distortion_FIX.c $(SILK)/src/SKP_Silk_NLSF_VQ_sum_error_FIX.c $(SILK)/src/SKP_Silk_NLSF_VQ_weights_laroia.c $(SILK)/src/SKP_Silk_NLSF_stabilize.c $(SILK)/src/SKP_Silk_NSQ.c $(SILK)/src/SKP_Silk_NSQ_del_dec.c $(SILK)/src/SKP_Silk_PLC.c $(SILK)/src/SKP_Silk_VAD.c $(SILK)/src/SKP_Silk_VQ_nearest_neighbor_FIX.c $(SILK)/src/SKP_Silk_allpass_int.c $(SILK)/src/SKP_Silk_ana_filt_bank_1.c $(SILK)/src/SKP_Silk_apply_sine_window.c $(SILK)/src/SKP_Silk_array_maxabs.c $(SILK)/src/SKP_Silk_autocorr.c $(SILK)/src/SKP_Silk_biquad.c $(SILK)/src/SKP_Silk_biquad_alt.c $(SILK)/src/SKP_Silk_burg_modified.c $(SILK)/src/SKP_Silk_bwexpander.c $(SILK)/src/SKP_Silk_bwexpander_32.c $(SILK)/src/SKP_Silk_code_signs.c $(SILK)/src/SKP_Silk_control_codec_FIX.c $(SILK)/src/SKP_Silk_corrMatrix_FIX.c $(SILK)/src/SKP_Silk_create_init_destroy.c $(SILK)/src/SKP_Silk_dec_API.c $(SILK)/src/SKP_Silk_decode_core.c $(SILK)/src/SKP_Silk_decode_frame.c $(SILK)/src/SKP_Silk_decode_indices_v4.c $(SILK)/src/SKP_Silk_decode_parameters.c $(SILK)/src/SKP_Silk_decode_parameters_v4.c $(SILK)/src/SKP_Silk_decode_pulses.c $(SILK)/src/SKP_Silk_decoder_set_fs.c $(SILK)/src/SKP_Silk_detect_SWB_input.c $(SILK)/src/SKP_Silk_enc_API.c $(SILK)/src/SKP_Silk_encode_frame_FIX.c $(SILK)/src/SKP_Silk_encode_parameters.c $(SILK)/src/SKP_Silk_encode_parameters_v4.c $(SILK)/src/SKP_Silk_encode_pulses.c $(SILK)/src/SKP_Silk_find_LPC_FIX.c $(SILK)/src/SKP_Silk_find_LTP_FIX.c $(SILK)/src/SKP_Silk_find_pitch_lags_FIX.c $(SILK)/src/SKP_Silk_find_pred_coefs_FIX.c $(SILK)/src/SKP_Silk_gain_quant.c $(SILK)/src/SKP_Silk_init_encoder_FIX.c $(SILK)/src/SKP_Silk_inner_prod_aligned.c $(SILK)/src/SKP_Silk_interpolate.c $(SILK)/src/SKP_Silk_k2a.c $(SILK)/src/SKP_Silk_k2a_Q16.c $(SILK)/src/SKP_Silk_lin2log.c $(SILK)/src/SKP_Silk_log2lin.c $(SILK)/src/SKP_Silk_lowpass_int.c $(SILK)/src/SKP_Silk_lowpass_short.c $(SILK)/src/SKP_Silk_noise_shape_analysis_FIX.c $(SILK)/src/SKP_Silk_pitch_analysis_core.c $(SILK)/src/SKP_Silk_pitch_est_tables.c $(SILK)/src/SKP_Silk_prefilter_FIX.c $(SILK)/src/SKP_Silk_process_NLSFs_FIX.c $(SILK)/src/SKP_Silk_process_gains_FIX.c $(SILK)/src/SKP_Silk_pulses_to_bytes.c $(SILK)/src/SKP_Silk_quant_LTP_gains_FIX.c $(SILK)/src/SKP_Silk_range_coder.c $(SILK)/src/SKP_Silk_regularize_correlations_FIX.c $(SILK)/src/SKP_Silk_resample_1_2.c $(SILK)/src/SKP_Silk_resample_1_2_coarse.c $(SILK)/src/SKP_Silk_resample_1_2_coarsest.c $(SILK)/src/SKP_Silk_resample_1_3.c $(SILK)/src/SKP_Silk_resample_2_1_coarse.c $(SILK)/src/SKP_Silk_resample_2_3.c $(SILK)/src/SKP_Silk_resample_2_3_coarse.c $(SILK)/src/SKP_Silk_resample_2_3_coarsest.c $(SILK)/src/SKP_Silk_resample_2_3_rom.c $(SILK)/src/SKP_Silk_resample_3_1.c $(SILK)/src/SKP_Silk_resample_3_2.c $(SILK)/src/SKP_Silk_resample_3_2_rom.c $(SILK)/src/SKP_Silk_resample_3_4.c $(SILK)/src/SKP_Silk_resample_4_3.c $(SILK)/src/SKP_Silk_residual_energy16_FIX.c $(SILK)/src/SKP_Silk_residual_energy_FIX.c $(SILK)/src/SKP_Silk_scale_copy_vector16.c $(SILK)/src/SKP_Silk_scale_vector.c $(SILK)/src/SKP_Silk_schur.c $(SILK)/src/SKP_Silk_schur64.c $(SILK)/src/SKP_Silk_shell_coder.c $(SILK)/src/SKP_Silk_sigm_Q15.c $(SILK)/src/SKP_Silk_solve_LS_FIX.c $(SILK)/src/SKP_Silk_sort.c $(SILK)/src/SKP_Silk_sum_sqr_shift.c $(SILK)/src/SKP_Silk_tables_LTP.c $(SILK)/src/SKP_Silk_tables_NLSF_CB0_10.c $(SILK)/src/SKP_Silk_tables_NLSF_CB0_16.c $(SILK)/src/SKP_Silk_tables_NLSF_CB1_10.c $(SILK)/src/SKP_Silk_tables_NLSF_CB1_16.c $(SILK)/src/SKP_Silk_tables_gain.c $(SILK)/src/SKP_Silk_tables_other.c $(SILK)/src/SKP_Silk_tables_pitch_lag.c $(SILK)/src/SKP_Silk_tables_pulses_per_block.c $(SILK)/src/SKP_Silk_tables_sign.c $(SILK)/src/SKP_Silk_tables_type_offset.c LOCAL_ARM_MODE := arm LOCAL_CFLAGS = -O3 LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(SILK)/src $(LOCAL_PATH)/$(SILK)/interface include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := silk8_jni LOCAL_SRC_FILES := silk8_jni.cpp LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(SILK)/src $(LOCAL_PATH)/$(SILK)/interface LOCAL_CFLAGS = -O3 LOCAL_STATIC_LIBRARIES := silkcommon LOCAL_ARM_MODE := arm LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := silk16_jni LOCAL_SRC_FILES := silk16_jni.cpp LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(SILK)/src $(LOCAL_PATH)/$(SILK)/interface LOCAL_CFLAGS = -O3 LOCAL_STATIC_LIBRARIES := silkcommon LOCAL_ARM_MODE := arm LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := silk24_jni LOCAL_SRC_FILES := silk24_jni.cpp LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(SILK)/src $(LOCAL_PATH)/$(SILK)/interface LOCAL_CFLAGS = -O3 LOCAL_STATIC_LIBRARIES := silkcommon LOCAL_ARM_MODE := arm LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog include $(BUILD_SHARED_LIBRARY)
5、创建JNI包装类silk8_jni.cpp、silk16_jni.cpp、silk24_jni.cpp,用来调用Silk中的C代码函数,编辑内容如下:
silk8_jni.cpp:
#include <jni.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> /* Define codec specific settings */ #define MAX_BYTES_ENC_PER_FRAME 250 // Equals peak bitrate of 100 kbps #define MAX_BYTES_DEC_PER_FRAME 1024 #define MAX_INPUT_FRAMES 5 #define MAX_LBRR_DELAY 2 #define MAX_FRAME_LENGTH 480 #define MAX_FRAME 160 #include <android/log.h> #define LOG_TAG "silk" // text for log tag #include "SKP_Silk_SDK_API.h" #include "SKP_Silk_SigProc_FIX.h" #undef DEBUG_SILK8 // the header length of the RTP frame (must skip when en/decoding) #define RTP_HDR_SIZE 12 static int codec_open = 0; static JavaVM *gJavaVM; const char *kInterfacePath = "org/sipdroid/pjlib/silk8"; /* encoder parameters */ SKP_int32 encSizeBytes; void *psEnc; /* default settings */ SKP_int fs_kHz = 8; SKP_int targetRate_bps = 20000; SKP_int packetSize_ms = 20; SKP_int frameSizeReadFromFile_ms = 20; SKP_int packetLoss_perc = 0, smplsSinceLastPacket; SKP_int INBandFec_enabled = 0, DTX_enabled = 0, quiet = 0; SKP_SILK_SDK_EncControlStruct encControl; // Struct for input to encoder /* decoder parameters */ jbyte payloadToDec[ MAX_BYTES_DEC_PER_FRAME * MAX_INPUT_FRAMES * ( MAX_LBRR_DELAY + 1 ) ]; jshort out[ ( MAX_FRAME_LENGTH << 1 ) * MAX_INPUT_FRAMES ], *outPtr; SKP_int32 decSizeBytes; void *psDec; SKP_SILK_SDK_DecControlStruct DecControl; extern "C" JNIEXPORT jint JNICALL Java_com_trunkbow_silk_SILK8_open (JNIEnv *env, jobject obj, jint compression) { int ret; if (codec_open++ != 0) return (jint)0; /* Set the samplingrate that is requested for the output */ DecControl.sampleRate = 8000; /* Create decoder */ ret = SKP_Silk_SDK_Get_Decoder_Size( &decSizeBytes ); if( ret ) { __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "n!!!!!!!! SKP_Silk_SDK_Get_Decoder_Size returned %d", ret ); } #ifdef DEBUG_SILK8 __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "### INIT Decoder decSizeBytes = %dn", decSizeBytes); #endif psDec = malloc( decSizeBytes ); /* Reset decoder */ ret = SKP_Silk_SDK_InitDecoder( psDec ); if( ret ) { __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "n!!!!!!!! SKP_Silk_InitDecoder returned %d", ret ); } /* Create Encoder */ ret = SKP_Silk_SDK_Get_Encoder_Size( &encSizeBytes ); if( ret ) { __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "n!!!!!!!! SKP_Silk_SDK_Get_Encoder_Size returned %d", ret ); } #ifdef DEBUG_SILK8 __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "### INIT Encoder encSizeBytes = %dn", encSizeBytes); #endif psEnc = malloc( encSizeBytes ); /* Reset Encoder */ ret = SKP_Silk_SDK_InitEncoder( psEnc, &encControl ); if( ret ) { __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "n!!!!!!!! SKP_Silk_SDK_InitEncoder returned %d", ret ); } /* Set Encoder parameters */ encControl.sampleRate = fs_kHz * 1000; encControl.packetSize = packetSize_ms * fs_kHz; encControl.packetLossPercentage = packetLoss_perc; encControl.useInBandFEC = INBandFec_enabled; encControl.useDTX = DTX_enabled; encControl.complexity = compression; encControl.bitRate = targetRate_bps; return (jint)0; } void Print_Decode_Error_Msg(int errcode) { switch (errcode) { case SKP_SILK_DEC_WRONG_SAMPLING_FREQUENCY: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnOutput sampling frequency lower than internal decoded sampling frequencyn", errcode); break; case SKP_SILK_DEC_PAYLOAD_TOO_LARGE: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnPayload size exceeded the maximum allowed 1024 bytesn", errcode); break; case SKP_SILK_DEC_PAYLOAD_ERROR: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnPayload has bit errorsn", errcode); break; } } void Print_Encode_Error_Msg(int errcode) { switch (errcode) { case SKP_SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnInput length is not a multiplum of 10 ms, or length is longer than the packet lengthn", errcode); break; case SKP_SILK_ENC_FS_NOT_SUPPORTED: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnSampling frequency not 8000, 12000, 16000 or 24000 Hertz n", errcode); break; case SKP_SILK_ENC_PACKET_SIZE_NOT_SUPPORTED: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnPacket size not 20, 40, 60, 80 or 100 msn", errcode); break; case SKP_SILK_ENC_PAYLOAD_BUF_TOO_SHORT: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnAllocated payload buffer too short n", errcode); break; case SKP_SILK_ENC_WRONG_LOSS_RATE: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnLoss rate not between 0 and 100 percentn", errcode); break; case SKP_SILK_ENC_WRONG_COMPLEXITY_SETTING: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnComplexity setting not valid, use 0, 1 or 2n", errcode); break; case SKP_SILK_ENC_WRONG_INBAND_FEC_SETTING: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnInband FEC setting not valid, use 0 or 1n", errcode); break; case SKP_SILK_ENC_WRONG_DTX_SETTING: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnDTX setting not valid, use 0 or 1n", errcode); break; case SKP_SILK_ENC_INTERNAL_ERROR: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnInternal encoder errorn", errcode); break; } } extern "C" JNIEXPORT jint JNICALL Java_com_trunkbow_silk_SILK8_encode (JNIEnv *env, jobject obj, jshortArray lin, jint offset, jbyteArray encoded, jint size) { jbyte enc_payload[ MAX_BYTES_DEC_PER_FRAME * MAX_INPUT_FRAMES ]; jshort in[ MAX_FRAME_LENGTH * MAX_INPUT_FRAMES ]; int ret,i,frsz=MAX_FRAME; SKP_int16 nBytes; unsigned int lin_pos = 0; if (!codec_open) return 0; #ifdef DEBUG_SILK8 __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "encoding frame size: %dtoffset: %dn", size, offset); #endif for (i = 0; i < size; i+=MAX_FRAME) { #ifdef DEBUG_SILK8 __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "encoding frame size: %dtoffset: %d i: %dn", size, offset, i); #endif env->GetShortArrayRegion(lin, offset + i,frsz, in); /* max payload size */ nBytes = MAX_BYTES_ENC_PER_FRAME * MAX_INPUT_FRAMES; ret = SKP_Silk_SDK_Encode( psEnc, &encControl, in, (SKP_int16)frsz, (SKP_uint8 *)enc_payload, &nBytes ); if( ret ) { __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!! SKP_Silk_Encode returned: %dn", ret); Print_Encode_Error_Msg(ret); break; } #ifdef DEBUG_SILK8 __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "Enocded nBytes: %dn", nBytes); #endif /* Write payload */ env->SetByteArrayRegion(encoded, RTP_HDR_SIZE+ lin_pos, nBytes, enc_payload); lin_pos += nBytes; } #ifdef DEBUG_SILK8 __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "encoding **END** frame size: %dtoffset: %d i: %d lin_pos: %dn", size, offset, i, lin_pos); #endif return (jint)lin_pos; } extern "C" JNIEXPORT jint JNICALL Java_com_trunkbow_silk_SILK8_decode (JNIEnv *env, jobject obj, jbyteArray encoded, jshortArray lin, jint size) { jbyte buffer [MAX_BYTES_DEC_PER_FRAME * MAX_INPUT_FRAMES * ( MAX_LBRR_DELAY + 1 ) ]; jshort output_buffer[( MAX_FRAME_LENGTH << 1 ) * MAX_INPUT_FRAMES ]; // SKP_int16 *outPtr; int ret; SKP_int16 len; // int tot_len,frames; if (!codec_open) return 0; #ifdef DEBUG_SILK8 __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "##### BEGIN DECODE ******** decoding frame size: %dn", size); #endif env->GetByteArrayRegion(encoded, RTP_HDR_SIZE, size, buffer); // outPtr = output_buffer; // tot_len = 0; // frames = 0; // do { ret = SKP_Silk_SDK_Decode( psDec, &DecControl, 0,(SKP_uint8 *) buffer, size, output_buffer,&len ); if( ret ) { __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!! SKP_Silk_SDK_Decode returned: %dn", ret); Print_Decode_Error_Msg(ret); } #ifdef DEBUG_SILK8 __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "##### DECODED length: %dnt Frame #: %d", len); #endif // frames++; // outPtr += len; // tot_len += len; // } while( DecControl.moreInternalDecoderFrames ); env->SetShortArrayRegion(lin, 0, len,output_buffer); return (jint)len; } extern "C" JNIEXPORT void JNICALL Java_com_trunkbow_silk_SILK8_close (JNIEnv *env, jobject obj) { if (--codec_open != 0) return; /* Free decoder */ free( psDec ); /* Free Encoder */ free( psEnc ); }
silk_16.cpp:
#include <jni.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> /* Define codec specific settings */ #define MAX_BYTES_ENC_PER_FRAME 250 // Equals peak bitrate of 100 kbps #define MAX_BYTES_DEC_PER_FRAME 1024 #define MAX_INPUT_FRAMES 5 #define MAX_LBRR_DELAY 2 #define MAX_FRAME_LENGTH 480 #define MAX_FRAME 320 #include <android/log.h> #define LOG_TAG "silk" // text for log tag #include "SKP_Silk_SDK_API.h" #include "SKP_Silk_SigProc_FIX.h" #undef DEBUG_SILK16 // the header length of the RTP frame (must skip when en/decoding) #define RTP_HDR_SIZE 12 static int codec_open = 0; static JavaVM *gJavaVM; const char *kInterfacePath = "org/sipdroid/pjlib/silk16"; /* encoder parameters */ SKP_int32 encSizeBytes; void *psEnc; /* default settings */ SKP_int fs_kHz = 16; SKP_int targetRate_bps = 20000; SKP_int packetSize_ms = 20; SKP_int frameSizeReadFromFile_ms = 20; SKP_int packetLoss_perc = 0, smplsSinceLastPacket; SKP_int INBandFec_enabled = 0, DTX_enabled = 0, quiet = 0; SKP_SILK_SDK_EncControlStruct encControl; // Struct for input to encoder /* decoder parameters */ jbyte payloadToDec[ MAX_BYTES_DEC_PER_FRAME * MAX_INPUT_FRAMES * ( MAX_LBRR_DELAY + 1 ) ]; jshort out[ ( MAX_FRAME_LENGTH << 1 ) * MAX_INPUT_FRAMES ], *outPtr; SKP_int32 decSizeBytes; void *psDec; SKP_SILK_SDK_DecControlStruct DecControl; extern "C" JNIEXPORT jint JNICALL Java_com_trunkbow_silk_SILK16_open (JNIEnv *env, jobject obj, jint compression) { int ret; if (codec_open++ != 0) return (jint)0; /* Set the samplingrate that is requested for the output */ DecControl.sampleRate = 16000; /* Create decoder */ ret = SKP_Silk_SDK_Get_Decoder_Size( &decSizeBytes ); if( ret ) { __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "n!!!!!!!! SKP_Silk_SDK_Get_Decoder_Size returned %d", ret ); } #ifdef DEBUG_SILK16 __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "### INIT Decoder decSizeBytes = %dn", decSizeBytes); #endif psDec = malloc( decSizeBytes ); /* Reset decoder */ ret = SKP_Silk_SDK_InitDecoder( psDec ); if( ret ) { __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "n!!!!!!!! SKP_Silk_InitDecoder returned %d", ret ); } /* Create Encoder */ ret = SKP_Silk_SDK_Get_Encoder_Size( &encSizeBytes ); if( ret ) { __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "n!!!!!!!! SKP_Silk_SDK_Get_Encoder_Size returned %d", ret ); } #ifdef DEBUG_SILK16 __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "### INIT Encoder encSizeBytes = %dn", encSizeBytes); #endif psEnc = malloc( encSizeBytes ); /* Reset Encoder */ ret = SKP_Silk_SDK_InitEncoder( psEnc, &encControl ); if( ret ) { __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "n!!!!!!!! SKP_Silk_SDK_InitEncoder returned %d", ret ); } /* Set Encoder parameters */ encControl.sampleRate = fs_kHz * 1000; encControl.packetSize = packetSize_ms * fs_kHz; encControl.packetLossPercentage = packetLoss_perc; encControl.useInBandFEC = INBandFec_enabled; encControl.useDTX = DTX_enabled; encControl.complexity = compression; encControl.bitRate = targetRate_bps; return (jint)0; } void Print_Decode_Error_Msg(int errcode) { switch (errcode) { case SKP_SILK_DEC_WRONG_SAMPLING_FREQUENCY: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnOutput sampling frequency lower than internal decoded sampling frequencyn", errcode); break; case SKP_SILK_DEC_PAYLOAD_TOO_LARGE: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnPayload size exceeded the maximum allowed 1024 bytesn", errcode); break; case SKP_SILK_DEC_PAYLOAD_ERROR: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnPayload has bit errorsn", errcode); break; } } void Print_Encode_Error_Msg(int errcode) { switch (errcode) { case SKP_SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnInput length is not a multiplum of 10 ms, or length is longer than the packet lengthn", errcode); break; case SKP_SILK_ENC_FS_NOT_SUPPORTED: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnSampling frequency not 8000, 12000, 16000 or 24000 Hertz n", errcode); break; case SKP_SILK_ENC_PACKET_SIZE_NOT_SUPPORTED: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnPacket size not 20, 40, 60, 80 or 100 msn", errcode); break; case SKP_SILK_ENC_PAYLOAD_BUF_TOO_SHORT: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnAllocated payload buffer too short n", errcode); break; case SKP_SILK_ENC_WRONG_LOSS_RATE: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnLoss rate not between 0 and 100 percentn", errcode); break; case SKP_SILK_ENC_WRONG_COMPLEXITY_SETTING: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnComplexity setting not valid, use 0, 1 or 2n", errcode); break; case SKP_SILK_ENC_WRONG_INBAND_FEC_SETTING: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnInband FEC setting not valid, use 0 or 1n", errcode); break; case SKP_SILK_ENC_WRONG_DTX_SETTING: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnDTX setting not valid, use 0 or 1n", errcode); break; case SKP_SILK_ENC_INTERNAL_ERROR: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnInternal encoder errorn", errcode); break; } } extern "C" JNIEXPORT jint JNICALL Java_com_trunkbow_silk_SILK16_encode (JNIEnv *env, jobject obj, jshortArray lin, jint offset, jbyteArray encoded, jint size) { jbyte enc_payload[ MAX_BYTES_DEC_PER_FRAME * MAX_INPUT_FRAMES ]; jshort in[ MAX_FRAME_LENGTH * MAX_INPUT_FRAMES ]; int ret,i,frsz=MAX_FRAME; SKP_int16 nBytes; unsigned int lin_pos = 0; if (!codec_open) return 0; #ifdef DEBUG_SILK16 __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "encoding frame size: %dtoffset: %dn", size, offset); #endif for (i = 0; i < size; i+=MAX_FRAME) { #ifdef DEBUG_SILK16 __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "encoding frame size: %dtoffset: %d i: %dn", size, offset, i); #endif env->GetShortArrayRegion(lin, offset + i,frsz, in); /* max payload size */ nBytes = MAX_BYTES_ENC_PER_FRAME * MAX_INPUT_FRAMES; ret = SKP_Silk_SDK_Encode( psEnc, &encControl, in, (SKP_int16)frsz, (SKP_uint8 *)enc_payload, &nBytes ); if( ret ) { __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!! SKP_Silk_Encode returned: %dn", ret); Print_Encode_Error_Msg(ret); break; } #ifdef DEBUG_SILK16 __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "Enocded nBytes: %dn", nBytes); #endif /* Write payload */ env->SetByteArrayRegion(encoded, RTP_HDR_SIZE+ lin_pos, nBytes, enc_payload); lin_pos += nBytes; } #ifdef DEBUG_SILK16 __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "encoding **END** frame size: %dtoffset: %d i: %d lin_pos: %dn", size, offset, i, lin_pos); #endif return (jint)lin_pos; } extern "C" JNIEXPORT jint JNICALL Java_com_trunkbow_silk_SILK16_decode (JNIEnv *env, jobject obj, jbyteArray encoded, jshortArray lin, jint size) { jbyte buffer [MAX_BYTES_DEC_PER_FRAME * MAX_INPUT_FRAMES * ( MAX_LBRR_DELAY + 1 ) ]; jshort output_buffer[( MAX_FRAME_LENGTH << 1 ) * MAX_INPUT_FRAMES ]; // SKP_int16 *outPtr; int ret; SKP_int16 len; // int tot_len,frames; if (!codec_open) return 0; #ifdef DEBUG_SILK16 __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "##### BEGIN DECODE ******** decoding frame size: %dn", size); #endif env->GetByteArrayRegion(encoded, RTP_HDR_SIZE, size, buffer); // outPtr = output_buffer; // tot_len = 0; // frames = 0; // do { ret = SKP_Silk_SDK_Decode( psDec, &DecControl, 0,(SKP_uint8 *) buffer, size, output_buffer,&len ); if( ret ) { __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!! SKP_Silk_SDK_Decode returned: %dn", ret); Print_Decode_Error_Msg(ret); } #ifdef DEBUG_SILK16 __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "##### DECODED length: %dnt Frame #: %d", len); #endif // frames++; // outPtr += len; // tot_len += len; // } while( DecControl.moreInternalDecoderFrames ); env->SetShortArrayRegion(lin, 0, len,output_buffer); return (jint)len; } extern "C" JNIEXPORT void JNICALL Java_com_trunkbow_silk_SILK16_close (JNIEnv *env, jobject obj) { if (--codec_open != 0) return; /* Free decoder */ free( psDec ); /* Free Encoder */ free( psEnc ); }
silk24_jni.cpp
#include <jni.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> /* Define codec specific settings */ #define MAX_BYTES_ENC_PER_FRAME 250 // Equals peak bitrate of 100 kbps #define MAX_BYTES_DEC_PER_FRAME 1024 #define MAX_INPUT_FRAMES 5 #define MAX_LBRR_DELAY 2 #define MAX_FRAME_LENGTH 480 #define MAX_FRAME 480 #include <android/log.h> #define LOG_TAG "silk" // text for log tag #include "SKP_Silk_SDK_API.h" #include "SKP_Silk_SigProc_FIX.h" #undef DEBUG_SILK24 // the header length of the RTP frame (must skip when en/decoding) #define RTP_HDR_SIZE 12 static int codec_open = 0; static JavaVM *gJavaVM; const char *kInterfacePath = "org/sipdroid/pjlib/SILK24"; /* encoder parameters */ SKP_int32 encSizeBytes; void *psEnc; /* default settings */ SKP_int fs_kHz = 24; SKP_int targetRate_bps = 20000; SKP_int packetSize_ms = 20; SKP_int frameSizeReadFromFile_ms = 20; SKP_int packetLoss_perc = 0, smplsSinceLastPacket; SKP_int INBandFec_enabled = 0, DTX_enabled = 0, quiet = 0; SKP_SILK_SDK_EncControlStruct encControl; // Struct for input to encoder /* decoder parameters */ jbyte payloadToDec[ MAX_BYTES_DEC_PER_FRAME * MAX_INPUT_FRAMES * ( MAX_LBRR_DELAY + 1 ) ]; jshort out[ ( MAX_FRAME_LENGTH << 1 ) * MAX_INPUT_FRAMES ], *outPtr; SKP_int32 decSizeBytes; void *psDec; SKP_SILK_SDK_DecControlStruct DecControl; extern "C" JNIEXPORT jint JNICALL Java_com_trunkbow_silk_SILK24_open (JNIEnv *env, jobject obj, jint compression) { int ret; if (codec_open++ != 0) return (jint)0; /* Set the samplingrate that is requested for the output */ DecControl.sampleRate = 24000; /* Create decoder */ ret = SKP_Silk_SDK_Get_Decoder_Size( &decSizeBytes ); if( ret ) { __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "n!!!!!!!! SKP_Silk_SDK_Get_Decoder_Size returned %d", ret ); } #ifdef DEBUG_SILK24 __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "### INIT Decoder decSizeBytes = %dn", decSizeBytes); #endif psDec = malloc( decSizeBytes ); /* Reset decoder */ ret = SKP_Silk_SDK_InitDecoder( psDec ); if( ret ) { __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "n!!!!!!!! SKP_Silk_InitDecoder returned %d", ret ); } /* Create Encoder */ ret = SKP_Silk_SDK_Get_Encoder_Size( &encSizeBytes ); if( ret ) { __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "n!!!!!!!! SKP_Silk_SDK_Get_Encoder_Size returned %d", ret ); } #ifdef DEBUG_SILK24 __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "### INIT Encoder encSizeBytes = %dn", encSizeBytes); #endif psEnc = malloc( encSizeBytes ); /* Reset Encoder */ ret = SKP_Silk_SDK_InitEncoder( psEnc, &encControl ); if( ret ) { __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "n!!!!!!!! SKP_Silk_SDK_InitEncoder returned %d", ret ); } /* Set Encoder parameters */ encControl.sampleRate = fs_kHz * 1000; encControl.packetSize = packetSize_ms * fs_kHz; encControl.packetLossPercentage = packetLoss_perc; encControl.useInBandFEC = INBandFec_enabled; encControl.useDTX = DTX_enabled; encControl.complexity = compression; encControl.bitRate = targetRate_bps; return (jint)0; } void Print_Decode_Error_Msg(int errcode) { switch (errcode) { case SKP_SILK_DEC_WRONG_SAMPLING_FREQUENCY: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnOutput sampling frequency lower than internal decoded sampling frequencyn", errcode); break; case SKP_SILK_DEC_PAYLOAD_TOO_LARGE: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnPayload size exceeded the maximum allowed 1024 bytesn", errcode); break; case SKP_SILK_DEC_PAYLOAD_ERROR: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnPayload has bit errorsn", errcode); break; } } void Print_Encode_Error_Msg(int errcode) { switch (errcode) { case SKP_SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnInput length is not a multiplum of 10 ms, or length is longer than the packet lengthn", errcode); break; case SKP_SILK_ENC_FS_NOT_SUPPORTED: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnSampling frequency not 8000, 12000, 16000 or 24000 Hertz n", errcode); break; case SKP_SILK_ENC_PACKET_SIZE_NOT_SUPPORTED: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnPacket size not 20, 40, 60, 80 or 100 msn", errcode); break; case SKP_SILK_ENC_PAYLOAD_BUF_TOO_SHORT: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnAllocated payload buffer too short n", errcode); break; case SKP_SILK_ENC_WRONG_LOSS_RATE: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnLoss rate not between 0 and 100 percentn", errcode); break; case SKP_SILK_ENC_WRONG_COMPLEXITY_SETTING: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnComplexity setting not valid, use 0, 1 or 2n", errcode); break; case SKP_SILK_ENC_WRONG_INBAND_FEC_SETTING: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnInband FEC setting not valid, use 0 or 1n", errcode); break; case SKP_SILK_ENC_WRONG_DTX_SETTING: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnDTX setting not valid, use 0 or 1n", errcode); break; case SKP_SILK_ENC_INTERNAL_ERROR: __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!!!!! Decode_Error_Message: %dnInternal encoder errorn", errcode); break; } } extern "C" JNIEXPORT jint JNICALL Java_com_trunkbow_silk_SILK24_encode (JNIEnv *env, jobject obj, jshortArray lin, jint offset, jbyteArray encoded, jint size) { jbyte enc_payload[ MAX_BYTES_DEC_PER_FRAME * MAX_INPUT_FRAMES ]; jshort in[ MAX_FRAME_LENGTH * MAX_INPUT_FRAMES ]; int ret,i,frsz=MAX_FRAME; SKP_int16 nBytes; unsigned int lin_pos = 0; if (!codec_open) return 0; #ifdef DEBUG_SILK24 __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "encoding frame size: %dtoffset: %dn", size, offset); #endif for (i = 0; i < size; i+=MAX_FRAME) { #ifdef DEBUG_SILK24 __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "encoding frame size: %dtoffset: %d i: %dn", size, offset, i); #endif env->GetShortArrayRegion(lin, offset + i,frsz, in); /* max payload size */ nBytes = MAX_BYTES_ENC_PER_FRAME * MAX_INPUT_FRAMES; ret = SKP_Silk_SDK_Encode( psEnc, &encControl, in, (SKP_int16)frsz, (SKP_uint8 *)enc_payload, &nBytes ); if( ret ) { __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!! SKP_Silk_Encode returned: %dn", ret); Print_Encode_Error_Msg(ret); break; } #ifdef DEBUG_SILK24 __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "Enocded nBytes: %dn", nBytes); #endif /* Write payload */ env->SetByteArrayRegion(encoded, RTP_HDR_SIZE+ lin_pos, nBytes, enc_payload); lin_pos += nBytes; } #ifdef DEBUG_SILK24 __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "encoding **END** frame size: %dtoffset: %d i: %d lin_pos: %dn", size, offset, i, lin_pos); #endif return (jint)lin_pos; } extern "C" JNIEXPORT jint JNICALL Java_com_trunkbow_silk_SILK24_decode (JNIEnv *env, jobject obj, jbyteArray encoded, jshortArray lin, jint size) { jbyte buffer [MAX_BYTES_DEC_PER_FRAME * MAX_INPUT_FRAMES * ( MAX_LBRR_DELAY + 1 ) ]; jshort output_buffer[( MAX_FRAME_LENGTH << 1 ) * MAX_INPUT_FRAMES ]; // SKP_int16 *outPtr; int ret; SKP_int16 len; // int tot_len,frames; if (!codec_open) return 0; #ifdef DEBUG_SILK24 __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "##### BEGIN DECODE ******** decoding frame size: %dn", size); #endif env->GetByteArrayRegion(encoded, RTP_HDR_SIZE, size, buffer); // outPtr = output_buffer; // tot_len = 0; // frames = 0; // do { ret = SKP_Silk_SDK_Decode( psDec, &DecControl, 0,(SKP_uint8 *) buffer, size, output_buffer,&len ); if( ret ) { __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "!!!!!!!! SKP_Silk_SDK_Decode returned: %dn", ret); Print_Decode_Error_Msg(ret); } #ifdef DEBUG_SILK24 __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "##### DECODED length: %dnt Frame #: %d", len); #endif // frames++; // outPtr += len; // tot_len += len; // } while( DecControl.moreInternalDecoderFrames ); env->SetShortArrayRegion(lin, 0, len,output_buffer); return (jint)len; } extern "C" JNIEXPORT void JNICALL Java_com_trunkbow_silk_SILK24_close (JNIEnv *env, jobject obj) { if (--codec_open != 0) return; /* Free decoder */ free( psDec ); /* Free Encoder */ free( psEnc ); }
6、在Java层创建Speex工具类,内容如下:
Silk8.java:
class SILK8{ /* * | fs (Hz) | BR (kbps) * ----------------+---------+--------- * Narrowband | 8000 | 6 -20 * Mediumband | 12000 | 7 -25 * Wideband | 16000 | 8 -30 * Super Wideband | 24000 | 12 -40 * * Table 1: fs specifies the audio sampling frequency in Hertz (Hz); BR * specifies the adaptive bit rate range in kilobits per second (kbps). * * Complexity can be scaled to optimize for CPU resources in real-time, * mostly in trade-off to network bit rate. 0 is least CPU demanding and * highest bit rate. */ private static final int DEFAULT_COMPLEXITY = 0; void load() { System.loadLibrary("silk8_jni"); } public native int open(int compression); 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(); }
Silk16.java:
class SILK16 { /* * | fs (Hz) | BR (kbps) * ----------------+---------+--------- * Narrowband | 8000 | 6 -20 * Mediumband | 12000 | 7 -25 * Wideband | 16000 | 8 -30 * Super Wideband | 24000 | 12 -40 * * Table 1: fs specifies the audio sampling frequency in Hertz (Hz); BR * specifies the adaptive bit rate range in kilobits per second (kbps). * * Complexity can be scaled to optimize for CPU resources in real-time, * mostly in trade-off to network bit rate. 0 is least CPU demanding and * highest bit rate. */ private static final int DEFAULT_COMPLEXITY = 0; void load() { System.loadLibrary("silk16_jni"); } public native int open(int compression); 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(); }
Silk24.java:
class SILK24 { /* * | fs (Hz) | BR (kbps) * ----------------+---------+--------- * Narrowband | 8000 | 6 -20 * Mediumband | 12000 | 7 -25 * Wideband | 16000 | 8 -30 * Super Wideband | 24000 | 12 -40 * * Table 1: fs specifies the audio sampling frequency in Hertz (Hz); BR * specifies the adaptive bit rate range in kilobits per second (kbps). * * Complexity can be scaled to optimize for CPU resources in real-time, * mostly in trade-off to network bit rate. 0 is least CPU demanding and * highest bit rate. */ private static final int DEFAULT_COMPLEXITY = 0; void load() { System.loadLibrary("silk24_jni"); } public native int open(int compression); 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(); }
7、使用cygwin编译,生成so文件。
三种VOIP语音算法比较
对比一下这三种VOIP语音算法的特点:
1、参数与特征
2、SILK性能
关于iLBC和Speex的性能可以参考以前写的文章。
3、关于VOIP一些观点(仅代表个人观点)
1)、Skype辛苦三年开发的SILK为什么要开源?
第一、技术上。其实SILK用到了很多Royalty Free的算法部分,遵循开源的法则,取之于民,用之于民;
第二、商业上。Skype根本不靠Codec赚钱,他本身最大的优势是网络条件做的比较好,这种环境下无论是SILK、iLBC还是G.729都可以达成比较不错的音质。
第三、口碑上。提高知名度,都已经把SILK定点好了,将来一定会有很多第三方去推广,广告、市场和口碑双赢;
2)、iLBC一定就比Speex质量好吗?
根据目前的评测,当二者的编码速率差不多时,iLBC的质量要好于Speex,但是也有一些牛人认为iLBC这种每帧独立编码的思想其实是低效率的,通过增加编码冗余也可以达到相同的音质,意思是将CELP编码器增加一些前后冗余信息适当提高码率,也可以提高丢包情况下的抗干扰性,这个暂时还没有定论。
3)、Speex总感觉用的人少?
到底是Speex的推广差、维护差,还是音质差?虽然Speex的开发者出来解释了很多原因,可是目前好像用的第三方不是很多,同时我也感觉它的音质好像是有一点差。
4)、有了SILK,还有人用iLBC吗?
SILK和iLBC来自两个不同公司skypeand GIPS,个人感觉会同时存在。