Android 音频 OpenSL ES 录音 采集

来源:互联网 发布:睡眠计时器软件 编辑:程序博客网 时间:2024/05/17 07:31



1,创建引擎
2,创建AudioRecorder并开始录音
3,暂停录音
4,释放资源
5,数据是通过回调函数处理的。

好处:缓冲区不用通过AudioRecord.getMinBufferSize获取,设置很小也能正常工作。比如设置80字节bytes
前面的文章我们讲解的是OpenSL ES音频播放,OpenSL ES非常强大,有音频播放当然有录音

下面我们编写OpenSL PCM录音,完成的功能是录制麦克风的声音存储PCM到data私有目录。


二、布局XML 创建文件 /res/layout/open_sl_audio_record.xml

[html]view plaincopy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent"  
  5.     android:orientation="vertical" >  
  6.   
  7.       
  8.     <Button  
  9.         android:id="@+id/start"  
  10.         android:layout_width="wrap_content"  
  11.         android:layout_height="wrap_content"  
  12.         android:text="开始" />  
  13.   
  14.        
  15.     <LinearLayout  
  16.         android:layout_width="match_parent"  
  17.         android:layout_height="wrap_content"  
  18.         android:orientation="horizontal" >  
  19.   
  20.         <Button  
  21.             android:id="@+id/stop"  
  22.             android:layout_width="wrap_content"  
  23.             android:layout_height="wrap_content"  
  24.             android:text="暂停" />  
  25.   
  26.     </LinearLayout>  
  27.   
  28. </LinearLayout>  
布局文件:开始和暂停两个Button控制录音

三、Activity类  创建/src/com/example/testopensl/AudioRecordActivity.java

[java]view plaincopy
  1. package com.example.testopensl;  
  2.   
  3. import java.io.File;  
  4.   
  5. import android.app.Activity;  
  6. import android.os.Bundle;  
  7. import android.view.View;  
  8. import android.view.View.OnClickListener;  
  9.   
  10. import com.example.audio.R;  
  11.   
  12. public class AudioRecordActivity extends Activity implements OnClickListener {  
  13.   
  14.     static {  
  15.         System.loadLibrary("TestAudio");  
  16.     }  
  17.     private boolean mRecording;  
  18.   
  19.     @Override  
  20.     protected void onCreate(Bundle savedInstanceState) {  
  21.         super.onCreate(savedInstanceState);  
  22.         setContentView(R.layout.open_sl_audio_record);  
  23.         findViewById(R.id.start).setOnClickListener(this);  
  24.         findViewById(R.id.stop).setOnClickListener(this);  
  25.         createEngine();  
  26.     }  
  27.   
  28.     @Override  
  29.     public void onClick(View v) {  
  30.         switch (v.getId()) {  
  31.         case R.id.start:  
  32.             File file = new File(getCacheDir(), "audio.pcm");  
  33.             createAudioRecord(file.getAbsolutePath());  
  34.             mRecording true;  
  35.             break;  
  36.         case R.id.stop:  
  37.             stop();  
  38.             mRecording false;  
  39.             break;  
  40.         }  
  41.     }  
  42.   
  43.     @Override  
  44.     protected void onDestroy() {  
  45.         super.onDestroy();  
  46.         if (mRecording) {  
  47.             stop();  
  48.         }  
  49.         shutdown();  
  50.     }  
  51.   
  52.     /** Native methods, implemented in jni folder */  
  53.     public native void createEngine();  
  54.   
  55.     public native void createAudioRecord(String uri);  
  56.   
  57.     public native void stop();  
  58.   
  59.     public native void shutdown();  
  60.   
  61. }  

文件存储位置:/data/data/com.example.audio/cache/audio.pcm

四、编写日志头文件,用于日志输出, 创建/jni/log.h 文件

[cpp]view plaincopy
  1. #ifndef LOG_H_  
  2. #define LOG_H_  
  3.   
  4. #include <android/log.h>  
  5.   
  6. #ifndef DGB  
  7. #define DGB 0  
  8. #endif  
  9.   
  10. #ifndef LOG_TAG  
  11. #define LOG_TAG __FILE__  
  12. #endif  
  13.   
  14. #ifndef ALOGD  
  15. #if DGB  
  16. #define ALOGD(...) \  
  17.         __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)  
  18. #else  
  19. #define ALOGD(...)   ((void)0)  
  20. #endif  
  21. #endif  
  22.   
  23. #endif /* LOG_H_ */  


六、JNI的实现 /jni/com_example_testopensl_AudioRecordActivity.cpp

[cpp]view plaincopy
  1. /** log */  
  2. #define LOG_TAG "NATVIE-AudioRecordActivity"  
  3. #define DGB 1  
  4.   
  5.   
  6. #include <jni.h>  
  7. #include "com_example_testopensl_AudioRecordActivity.h"  
  8.   
  9. #include "log.h"  
  10. #include <stdio.h>  
  11. #include <assert.h>  
  12.   
  13. #include <SLES/OpenSLES.h>  
  14. #include <SLES/OpenSLES_Android.h>  
  15.   
  16. /* Size of the recording buffer queue */  
  17. #define NB_BUFFERS_IN_QUEUE 1  
  18.   
  19. /* Explicitly requesting SL_IID_ANDROIDSIMPLEBUFFERQUEUE and SL_IID_ANDROIDCONFIGURATION 
  20.  * on the AudioRecorder object */  
  21. #define NUM_EXPLICIT_INTERFACES_FOR_RECORDER 2  
  22.   
  23. /* Size of the recording buffer queue */  
  24. #define NB_BUFFERS_IN_QUEUE 1  
  25. /* Size of each buffer in the queue */  
  26. #define BUFFER_SIZE_IN_SAMPLES 8192  
  27. #define BUFFER_SIZE_IN_BYTES   (2 * BUFFER_SIZE_IN_SAMPLES)  
  28.   
  29. /* Local storage for Audio data */  
  30. int8_t pcmData[NB_BUFFERS_IN_QUEUE * BUFFER_SIZE_IN_BYTES];  
  31.   
  32.   
  33. // engine interfaces  
  34. static SLObjectItf engineObject = NULL;  
  35. static SLEngineItf engineEngine = NULL;  
  36.   
  37. //audio record interfaces  
  38. static SLObjectItf recorderObject = NULL;  
  39. static SLRecordItf recordItf = NULL;  
  40. static SLAndroidSimpleBufferQueueItf recBuffQueueItf = NULL;  
  41. static SLAndroidConfigurationItf configItf = NULL;  
  42.   
  43. static FILE * gFile = NULL;  
  44.   
  45. //-----------------------------------------------------------------  
  46. /* Structure for passing information to callback function */  
  47. typedef struct CallbackCntxt_ {  
  48.     SLPlayItf  playItf;  
  49.     SLuint32   size;  
  50.     SLint8*   pDataBase;    // Base address of local audio data storage  
  51.     SLint8*   pData;        // Current address of local audio data storage  
  52. } CallbackCntxt;  
  53.   
  54. static CallbackCntxt cntxt;  
  55.   
  56. /* Callback for recording buffer queue events */  
  57. void recCallback(SLRecordItf caller, void *pContext, SLuint32 event) {  
  58.     if (SL_RECORDEVENT_HEADATNEWPOS & event) {  
  59.         SLmillisecond pMsec = 0;  
  60.         (*caller)->GetPosition(caller, &pMsec);  
  61.         ALOGD("SL_RECORDEVENT_HEADATNEWPOS current position=%ums\n", pMsec);  
  62.     }  
  63.   
  64.     if (SL_RECORDEVENT_HEADATMARKER & event) {  
  65.         SLmillisecond pMsec = 0;  
  66.         (*caller)->GetPosition(caller, &pMsec);  
  67.         ALOGD("SL_RECORDEVENT_HEADATMARKER current position=%ums\n", pMsec);  
  68.     }  
  69. }  
  70.   
  71. /* Callback for recording buffer queue events */  
  72. void recBufferQueueCallback(SLAndroidSimpleBufferQueueItf queueItf, void *pContext) {  
  73.   
  74.     CallbackCntxt *pCntxt = (CallbackCntxt*) pContext;  
  75.   
  76.     /* Save the recorded data  */  
  77.     fwrite(pCntxt->pDataBase, BUFFER_SIZE_IN_BYTES, 1, gFile);  
  78.   
  79.     /* Increase data pointer by buffer size */  
  80.     pCntxt->pData += BUFFER_SIZE_IN_BYTES;  
  81.   
  82.     if (pCntxt->pData >= pCntxt->pDataBase + (NB_BUFFERS_IN_QUEUE * BUFFER_SIZE_IN_BYTES)) {  
  83.         pCntxt->pData = pCntxt->pDataBase;  
  84.     }  
  85.   
  86.     (*queueItf)->Enqueue(queueItf, pCntxt->pDataBase, BUFFER_SIZE_IN_BYTES);  
  87.   
  88.     SLAndroidSimpleBufferQueueState recQueueState;  
  89.     (*queueItf)->GetState(queueItf, &recQueueState);  
  90.   
  91. }  
  92.   
  93.   
  94. /* 
  95.  * Class:     com_example_testopensl_AudioRecordActivity 
  96.  * Method:    createEngine 
  97.  * Signature: ()V 
  98.  */  
  99. JNIEXPORT void JNICALL Java_com_example_testopensl_AudioRecordActivity_createEngine(JNIEnv *, jobject) {  
  100.     SLEngineOption EngineOption[] = {  
  101.                 {(SLuint32) SL_ENGINEOPTION_THREADSAFE, (SLuint32) SL_BOOLEAN_TRUE}  
  102.         };  
  103.     SLresult result;  
  104.     result = slCreateEngine(&engineObject, 1, EngineOption, 0, NULL, NULL);  
  105.     assert(SL_RESULT_SUCCESS == result);  
  106.   
  107.      /* Realizing the SL Engine in synchronous mode. */  
  108.     result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);  
  109.     assert(SL_RESULT_SUCCESS == result);  
  110.   
  111.     // get the engine interface, which is needed in order to create other objects  
  112.     result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);  
  113.     assert(SL_RESULT_SUCCESS == result);  
  114. }  
  115.   
  116. /* 
  117.  * Class:     com_example_testopensl_AudioRecordActivity 
  118.  * Method:    createAudioRecord 
  119.  * Signature: (Ljava/lang/String;)V 
  120.  */  
  121. JNIEXPORT void JNICALL Java_com_example_testopensl_AudioRecordActivity_createAudioRecord(JNIEnv *env, jobject, jstring uri) {  
  122.   
  123.     if (recorderObject != NULL) {  
  124.   
  125.         ALOGD(" already create auido record");  
  126.         return ;  
  127.     }  
  128.   
  129.     const char* utf8 = env->GetStringUTFChars(uri, NULL);  
  130.     gFile = fopen(utf8, "w");  
  131.     env->ReleaseStringUTFChars(uri, utf8);  
  132.   
  133.     if (gFile == NULL) {  
  134.         ALOGD(" open file fail ");  
  135.         return ;  
  136.     }  
  137.   
  138.     SLresult result;  
  139.   
  140.     /* setup the data source*/  
  141.     SLDataLocator_IODevice ioDevice = {  
  142.             SL_DATALOCATOR_IODEVICE,  
  143.             SL_IODEVICE_AUDIOINPUT,  
  144.             SL_DEFAULTDEVICEID_AUDIOINPUT,  
  145.             NULL  
  146.     };  
  147.   
  148.     SLDataSource recSource = {&ioDevice, NULL};  
  149.   
  150.     SLDataLocator_AndroidSimpleBufferQueue recBufferQueue = {  
  151.             SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,  
  152.             NB_BUFFERS_IN_QUEUE  
  153.     };  
  154.   
  155.     SLDataFormat_PCM pcm = {  
  156.             SL_DATAFORMAT_PCM,  
  157.             2,  
  158.             SL_SAMPLINGRATE_44_1,  
  159.             SL_PCMSAMPLEFORMAT_FIXED_16,  
  160.             16,  
  161.             SL_SPEAKER_FRONT_LEFT| SL_SPEAKER_FRONT_RIGHT,  
  162.             SL_BYTEORDER_LITTLEENDIAN  
  163.     };  
  164.   
  165.     SLDataSink dataSink = { &recBufferQueue, &pcm };  
  166.     SLInterfaceID iids[NUM_EXPLICIT_INTERFACES_FOR_RECORDER] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION};  
  167.     SLboolean required[NUM_EXPLICIT_INTERFACES_FOR_RECORDER] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};  
  168.   
  169.     /* Create the audio recorder */  
  170.     result = (*engineEngine)->CreateAudioRecorder(engineEngine, &recorderObject , &recSource, &dataSink,  
  171.             NUM_EXPLICIT_INTERFACES_FOR_RECORDER, iids, required);  
  172.     assert(SL_RESULT_SUCCESS == result);  
  173.   
  174.   
  175.     /* get the android configuration interface*/  
  176.     result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDCONFIGURATION, &configItf);  
  177.     assert(SL_RESULT_SUCCESS == result);  
  178.   
  179.     /* Realize the recorder in synchronous mode. */  
  180.     result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE);  
  181.     assert(SL_RESULT_SUCCESS == result);  
  182.   
  183.     /* Get the buffer queue interface which was explicitly requested */  
  184.     result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, (void*) &recBuffQueueItf);  
  185.     assert(SL_RESULT_SUCCESS == result);  
  186.   
  187.   
  188.     /* get the record interface */  
  189.     result = (*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recordItf);  
  190.     assert(SL_RESULT_SUCCESS == result);  
  191.   
  192.   
  193.     /* Set up the recorder callback to get events during the recording */  
  194.     result = (*recordItf)->SetMarkerPosition(recordItf, 2000);  
  195.     assert(SL_RESULT_SUCCESS == result);  
  196.   
  197.     result = (*recordItf)->SetPositionUpdatePeriod(recordItf, 500);  
  198.     assert(SL_RESULT_SUCCESS == result);  
  199.   
  200.     result = (*recordItf)->SetCallbackEventsMask(recordItf,  
  201.                 SL_RECORDEVENT_HEADATMARKER | SL_RECORDEVENT_HEADATNEWPOS);  
  202.     assert(SL_RESULT_SUCCESS == result);  
  203.   
  204.     result = (*recordItf)->RegisterCallback(recordItf, recCallback, NULL);  
  205.     assert(SL_RESULT_SUCCESS == result);  
  206.   
  207.     /* Initialize the callback and its context for the recording buffer queue */  
  208.   
  209.     cntxt.pDataBase = (int8_t*) &pcmData;  
  210.     cntxt.pData = cntxt.pDataBase;  
  211.     cntxt.size = sizeof(pcmData);  
  212.     result = (*recBuffQueueItf)->RegisterCallback(recBuffQueueItf, recBufferQueueCallback, &cntxt);  
  213.     assert(SL_RESULT_SUCCESS == result);  
  214.   
  215.     /* Enqueue buffers to map the region of memory allocated to store the recorded data */  
  216.     ALOGD("Enqueueing buffer ");  
  217.     for (int i = 0; i < NB_BUFFERS_IN_QUEUE; i++) {  
  218.         ALOGD("%d ", i);  
  219.         result = (*recBuffQueueItf)->Enqueue(recBuffQueueItf, cntxt.pData, BUFFER_SIZE_IN_BYTES);  
  220.         assert(SL_RESULT_SUCCESS == result);  
  221.         cntxt.pData += BUFFER_SIZE_IN_BYTES;  
  222.     }  
  223.     cntxt.pData = cntxt.pDataBase;  
  224.   
  225.     /* Start recording */  
  226.     result = (*recordItf)->SetRecordState(recordItf, SL_RECORDSTATE_RECORDING);  
  227.     assert(SL_RESULT_SUCCESS == result);  
  228.     ALOGD("Starting to record");  
  229.   
  230. }  
  231.   
  232. /* 
  233.  * Class:     com_example_testopensl_AudioRecordActivity 
  234.  * Method:    stop 
  235.  * Signature: ()V 
  236.  */  
  237. JNIEXPORT void JNICALL Java_com_example_testopensl_AudioRecordActivity_stop(JNIEnv *, jobject) {  
  238.     if (recordItf != NULL) {  
  239.         SLresult result = (*recordItf)->SetRecordState(recordItf, SL_RECORDSTATE_STOPPED);  
  240.         assert(SL_RESULT_SUCCESS == result);  
  241.     }  
  242. }  
  243.   
  244. /* 
  245.  * Class:     com_example_testopensl_AudioRecordActivity 
  246.  * Method:    shutdown 
  247.  * Signature: ()V 
  248.  */  
  249. JNIEXPORT void JNICALL Java_com_example_testopensl_AudioRecordActivity_shutdown(JNIEnv *, jobject) {  
  250.   
  251.     //destroy recorder object , and invlidate all associated interfaces  
  252.     if (recorderObject != NULL) {  
  253.         (*recorderObject)->Destroy(recorderObject);  
  254.         recorderObject = NULL;  
  255.         recordItf = NULL;  
  256.         recBuffQueueItf = NULL;  
  257.         configItf = NULL;  
  258.     }  
  259.   
  260.     // destroy engine object, and invalidate all associated interfaces  
  261.     if (engineObject != NULL) {  
  262.         (*engineObject)->Destroy(engineObject);  
  263.         engineObject = NULL;  
  264.         engineEngine = NULL;  
  265.     }  
  266.   
  267.     //colse the file  
  268.     if (gFile != NULL) {  
  269.         fclose(gFile);  
  270.         gFile == NULL;  
  271.     }  
  272. }  
方法说明:
        Java_com_example_testopensl_AudioRecordActivity_createEngine 创建引擎
        Java_com_example_testopensl_AudioRecordActivity_createAudioRecord创建AudioRecorder并开始录音
        Java_com_example_testopensl_AudioRecordActivity_stop 暂停录音
        Java_com_example_testopensl_AudioRecordActivity_shutdown 释放资源
七、创建/jni/Android.mk 编译文件

[plain]view plaincopy
  1. LOCAL_PATH := $(call my-dir)  
  2.   
  3. include $(CLEAR_VARS)  
  4.   
  5. LOCAL_MODULE    := TestAudio  
  6. LOCAL_SRC_FILES := com_example_testopensl_AudioRecordActivity.cpp  
  7. LOCAL_LDLIBS += -llog -lOpenSLES -landroid  
  8.   
  9. include $(BUILD_SHARED_LIBRARY)  
LOCAL_LDLIBS 需要加llog、lOpenSLES、landroid 编译链接库

八、AndroidManifest 配置

录音时记得加上权限

[html]view plaincopy
  1. <uses-permission android:name="android.permission.RECORD_AUDIO" />  
编写完Eclipse 结构图如下:




运行之后界面图如下:



代码编写完毕.点击开始就可以录音

在代码中需要注意的是录音的缓冲区大小,缓存区太小会导致录音失败(每个设备的最小缓冲区是不一样的),太大当音频实时传输会延迟比较大。这时候可以使用Java API获取最小的缓冲区,文中是写死的2*8192,在实际项目中建议使用API获取,下面是Java API

[java]view plaincopy
  1. int sampleRateInHz = 44100;  
  2. int channelConfig = AudioFormat.CHANNEL_OUT_STEREO;  
  3. int audioFormat = AudioFormat.ENCODING_PCM_16BIT;  
  4. int bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat) * 2;  

这个可以通过jni访问java类AudioRecord实现,从而封装代码。



1,创建引擎
2,创建AudioRecorder并开始录音
3,暂停录音
4,释放资源
5,数据是通过回调函数处理的。

好处:缓冲区不用通过AudioRecord.getMinBufferSize获取,设置很小也能正常工作。比如设置80字节bytes
前面的文章我们讲解的是OpenSL ES音频播放,OpenSL ES非常强大,有音频播放当然有录音

下面我们编写OpenSL PCM录音,完成的功能是录制麦克风的声音存储PCM到data私有目录。


二、布局XML 创建文件 /res/layout/open_sl_audio_record.xml

[html]view plaincopy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent"  
  5.     android:orientation="vertical" >  
  6.   
  7.       
  8.     <Button  
  9.         android:id="@+id/start"  
  10.         android:layout_width="wrap_content"  
  11.         android:layout_height="wrap_content"  
  12.         android:text="开始" />  
  13.   
  14.        
  15.     <LinearLayout  
  16.         android:layout_width="match_parent"  
  17.         android:layout_height="wrap_content"  
  18.         android:orientation="horizontal" >  
  19.   
  20.         <Button  
  21.             android:id="@+id/stop"  
  22.             android:layout_width="wrap_content"  
  23.             android:layout_height="wrap_content"  
  24.             android:text="暂停" />  
  25.   
  26.     </LinearLayout>  
  27.   
  28. </LinearLayout>  
布局文件:开始和暂停两个Button控制录音

三、Activity类  创建/src/com/example/testopensl/AudioRecordActivity.java

[java]view plaincopy
  1. package com.example.testopensl;  
  2.   
  3. import java.io.File;  
  4.   
  5. import android.app.Activity;  
  6. import android.os.Bundle;  
  7. import android.view.View;  
  8. import android.view.View.OnClickListener;  
  9.   
  10. import com.example.audio.R;  
  11.   
  12. public class AudioRecordActivity extends Activity implements OnClickListener {  
  13.   
  14.     static {  
  15.         System.loadLibrary("TestAudio");  
  16.     }  
  17.     private boolean mRecording;  
  18.   
  19.     @Override  
  20.     protected void onCreate(Bundle savedInstanceState) {  
  21.         super.onCreate(savedInstanceState);  
  22.         setContentView(R.layout.open_sl_audio_record);  
  23.         findViewById(R.id.start).setOnClickListener(this);  
  24.         findViewById(R.id.stop).setOnClickListener(this);  
  25.         createEngine();  
  26.     }  
  27.   
  28.     @Override  
  29.     public void onClick(View v) {  
  30.         switch (v.getId()) {  
  31.         case R.id.start:  
  32.             File file = new File(getCacheDir(), "audio.pcm");  
  33.             createAudioRecord(file.getAbsolutePath());  
  34.             mRecording true;  
  35.             break;  
  36.         case R.id.stop:  
  37.             stop();  
  38.             mRecording false;  
  39.             break;  
  40.         }  
  41.     }  
  42.   
  43.     @Override  
  44.     protected void onDestroy() {  
  45.         super.onDestroy();  
  46.         if (mRecording) {  
  47.             stop();  
  48.         }  
  49.         shutdown();  
  50.     }  
  51.   
  52.     /** Native methods, implemented in jni folder */  
  53.     public native void createEngine();  
  54.   
  55.     public native void createAudioRecord(String uri);  
  56.   
  57.     public native void stop();  
  58.   
  59.     public native void shutdown();  
  60.   
  61. }  

文件存储位置:/data/data/com.example.audio/cache/audio.pcm

四、编写日志头文件,用于日志输出, 创建/jni/log.h 文件

[cpp]view plaincopy
  1. #ifndef LOG_H_  
  2. #define LOG_H_  
  3.   
  4. #include <android/log.h>  
  5.   
  6. #ifndef DGB  
  7. #define DGB 0  
  8. #endif  
  9.   
  10. #ifndef LOG_TAG  
  11. #define LOG_TAG __FILE__  
  12. #endif  
  13.   
  14. #ifndef ALOGD  
  15. #if DGB  
  16. #define ALOGD(...) \  
  17.         __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)  
  18. #else  
  19. #define ALOGD(...)   ((void)0)  
  20. #endif  
  21. #endif  
  22.   
  23. #endif /* LOG_H_ */  


六、JNI的实现 /jni/com_example_testopensl_AudioRecordActivity.cpp

[cpp]view plaincopy
  1. /** log */  
  2. #define LOG_TAG "NATVIE-AudioRecordActivity"  
  3. #define DGB 1  
  4.   
  5.   
  6. #include <jni.h>  
  7. #include "com_example_testopensl_AudioRecordActivity.h"  
  8.   
  9. #include "log.h"  
  10. #include <stdio.h>  
  11. #include <assert.h>  
  12.   
  13. #include <SLES/OpenSLES.h>  
  14. #include <SLES/OpenSLES_Android.h>  
  15.   
  16. /* Size of the recording buffer queue */  
  17. #define NB_BUFFERS_IN_QUEUE 1  
  18.   
  19. /* Explicitly requesting SL_IID_ANDROIDSIMPLEBUFFERQUEUE and SL_IID_ANDROIDCONFIGURATION 
  20.  * on the AudioRecorder object */  
  21. #define NUM_EXPLICIT_INTERFACES_FOR_RECORDER 2  
  22.   
  23. /* Size of the recording buffer queue */  
  24. #define NB_BUFFERS_IN_QUEUE 1  
  25. /* Size of each buffer in the queue */  
  26. #define BUFFER_SIZE_IN_SAMPLES 8192  
  27. #define BUFFER_SIZE_IN_BYTES   (2 * BUFFER_SIZE_IN_SAMPLES)  
  28.   
  29. /* Local storage for Audio data */  
  30. int8_t pcmData[NB_BUFFERS_IN_QUEUE * BUFFER_SIZE_IN_BYTES];  
  31.   
  32.   
  33. // engine interfaces  
  34. static SLObjectItf engineObject = NULL;  
  35. static SLEngineItf engineEngine = NULL;  
  36.   
  37. //audio record interfaces  
  38. static SLObjectItf recorderObject = NULL;  
  39. static SLRecordItf recordItf = NULL;  
  40. static SLAndroidSimpleBufferQueueItf recBuffQueueItf = NULL;  
  41. static SLAndroidConfigurationItf configItf = NULL;  
  42.   
  43. static FILE * gFile = NULL;  
  44.   
  45. //-----------------------------------------------------------------  
  46. /* Structure for passing information to callback function */  
  47. typedef struct CallbackCntxt_ {  
  48.     SLPlayItf  playItf;  
  49.     SLuint32   size;  
  50.     SLint8*   pDataBase;    // Base address of local audio data storage  
  51.     SLint8*   pData;        // Current address of local audio data storage  
  52. } CallbackCntxt;  
  53.   
  54. static CallbackCntxt cntxt;  
  55.   
  56. /* Callback for recording buffer queue events */  
  57. void recCallback(SLRecordItf caller, void *pContext, SLuint32 event) {  
  58.     if (SL_RECORDEVENT_HEADATNEWPOS & event) {  
  59.         SLmillisecond pMsec = 0;  
  60.         (*caller)->GetPosition(caller, &pMsec);  
  61.         ALOGD("SL_RECORDEVENT_HEADATNEWPOS current position=%ums\n", pMsec);  
  62.     }  
  63.   
  64.     if (SL_RECORDEVENT_HEADATMARKER & event) {  
  65.         SLmillisecond pMsec = 0;  
  66.         (*caller)->GetPosition(caller, &pMsec);  
  67.         ALOGD("SL_RECORDEVENT_HEADATMARKER current position=%ums\n", pMsec);  
  68.     }  
  69. }  
  70.   
  71. /* Callback for recording buffer queue events */  
  72. void recBufferQueueCallback(SLAndroidSimpleBufferQueueItf queueItf, void *pContext) {  
  73.   
  74.     CallbackCntxt *pCntxt = (CallbackCntxt*) pContext;  
  75.   
  76.     /* Save the recorded data  */  
  77.     fwrite(pCntxt->pDataBase, BUFFER_SIZE_IN_BYTES, 1, gFile);  
  78.   
  79.     /* Increase data pointer by buffer size */  
  80.     pCntxt->pData += BUFFER_SIZE_IN_BYTES;  
  81.   
  82.     if (pCntxt->pData >= pCntxt->pDataBase + (NB_BUFFERS_IN_QUEUE * BUFFER_SIZE_IN_BYTES)) {  
  83.         pCntxt->pData = pCntxt->pDataBase;  
  84.     }  
  85.   
  86.     (*queueItf)->Enqueue(queueItf, pCntxt->pDataBase, BUFFER_SIZE_IN_BYTES);  
  87.   
  88.     SLAndroidSimpleBufferQueueState recQueueState;  
  89.     (*queueItf)->GetState(queueItf, &recQueueState);  
  90.   
  91. }  
  92.   
  93.   
  94. /* 
  95.  * Class:     com_example_testopensl_AudioRecordActivity 
  96.  * Method:    createEngine 
  97.  * Signature: ()V 
  98.  */  
  99. JNIEXPORT void JNICALL Java_com_example_testopensl_AudioRecordActivity_createEngine(JNIEnv *, jobject) {  
  100.     SLEngineOption EngineOption[] = {  
  101.                 {(SLuint32) SL_ENGINEOPTION_THREADSAFE, (SLuint32) SL_BOOLEAN_TRUE}  
  102.         };  
  103.     SLresult result;  
  104.     result = slCreateEngine(&engineObject, 1, EngineOption, 0, NULL, NULL);  
  105.     assert(SL_RESULT_SUCCESS == result);  
  106.   
  107.      /* Realizing the SL Engine in synchronous mode. */  
  108.     result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);  
  109.     assert(SL_RESULT_SUCCESS == result);  
  110.   
  111.     // get the engine interface, which is needed in order to create other objects  
  112.     result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);  
  113.     assert(SL_RESULT_SUCCESS == result);  
  114. }  
  115.   
  116. /* 
  117.  * Class:     com_example_testopensl_AudioRecordActivity 
  118.  * Method:    createAudioRecord 
  119.  * Signature: (Ljava/lang/String;)V 
  120.  */  
  121. JNIEXPORT void JNICALL Java_com_example_testopensl_AudioRecordActivity_createAudioRecord(JNIEnv *env, jobject, jstring uri) {  
  122.   
  123.     if (recorderObject != NULL) {  
  124.   
  125.         ALOGD(" already create auido record");  
  126.         return ;  
  127.     }  
  128.   
  129.     const char* utf8 = env->GetStringUTFChars(uri, NULL);  
  130.     gFile = fopen(utf8, "w");  
  131.     env->ReleaseStringUTFChars(uri, utf8);  
  132.   
  133.     if (gFile == NULL) {  
  134.         ALOGD(" open file fail ");  
  135.         return ;  
  136.     }  
  137.   
  138.     SLresult result;  
  139.   
  140.     /* setup the data source*/  
  141.     SLDataLocator_IODevice ioDevice = {  
  142.             SL_DATALOCATOR_IODEVICE,  
  143.             SL_IODEVICE_AUDIOINPUT,  
  144.             SL_DEFAULTDEVICEID_AUDIOINPUT,  
  145.             NULL  
  146.     };  
  147.   
  148.     SLDataSource recSource = {&ioDevice, NULL};  
  149.   
  150.     SLDataLocator_AndroidSimpleBufferQueue recBufferQueue = {  
  151.             SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,  
  152.             NB_BUFFERS_IN_QUEUE  
  153.     };  
  154.   
  155.     SLDataFormat_PCM pcm = {  
  156.             SL_DATAFORMAT_PCM,  
  157.             2,  
  158.             SL_SAMPLINGRATE_44_1,  
  159.             SL_PCMSAMPLEFORMAT_FIXED_16,  
  160.             16,  
  161.             SL_SPEAKER_FRONT_LEFT| SL_SPEAKER_FRONT_RIGHT,  
  162.             SL_BYTEORDER_LITTLEENDIAN  
  163.     };  
  164.   
  165.     SLDataSink dataSink = { &recBufferQueue, &pcm };  
  166.     SLInterfaceID iids[NUM_EXPLICIT_INTERFACES_FOR_RECORDER] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION};  
  167.     SLboolean required[NUM_EXPLICIT_INTERFACES_FOR_RECORDER] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};  
  168.   
  169.     /* Create the audio recorder */  
  170.     result = (*engineEngine)->CreateAudioRecorder(engineEngine, &recorderObject , &recSource, &dataSink,  
  171.             NUM_EXPLICIT_INTERFACES_FOR_RECORDER, iids, required);  
  172.     assert(SL_RESULT_SUCCESS == result);  
  173.   
  174.   
  175.     /* get the android configuration interface*/  
  176.     result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDCONFIGURATION, &configItf);  
  177.     assert(SL_RESULT_SUCCESS == result);  
  178.   
  179.     /* Realize the recorder in synchronous mode. */  
  180.     result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE);  
  181.     assert(SL_RESULT_SUCCESS == result);  
  182.   
  183.     /* Get the buffer queue interface which was explicitly requested */  
  184.     result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, (void*) &recBuffQueueItf);  
  185.     assert(SL_RESULT_SUCCESS == result);  
  186.   
  187.   
  188.     /* get the record interface */  
  189.     result = (*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recordItf);  
  190.     assert(SL_RESULT_SUCCESS == result);  
  191.   
  192.   
  193.     /* Set up the recorder callback to get events during the recording */  
  194.     result = (*recordItf)->SetMarkerPosition(recordItf, 2000);  
  195.     assert(SL_RESULT_SUCCESS == result);  
  196.   
  197.     result = (*recordItf)->SetPositionUpdatePeriod(recordItf, 500);  
  198.     assert(SL_RESULT_SUCCESS == result);  
  199.   
  200.     result = (*recordItf)->SetCallbackEventsMask(recordItf,  
  201.                 SL_RECORDEVENT_HEADATMARKER | SL_RECORDEVENT_HEADATNEWPOS);  
  202.     assert(SL_RESULT_SUCCESS == result);  
  203.   
  204.     result = (*recordItf)->RegisterCallback(recordItf, recCallback, NULL);  
  205.     assert(SL_RESULT_SUCCESS == result);  
  206.   
  207.     /* Initialize the callback and its context for the recording buffer queue */  
  208.   
  209.     cntxt.pDataBase = (int8_t*) &pcmData;  
  210.     cntxt.pData = cntxt.pDataBase;  
  211.     cntxt.size = sizeof(pcmData);  
  212.     result = (*recBuffQueueItf)->RegisterCallback(recBuffQueueItf, recBufferQueueCallback, &cntxt);  
  213.     assert(SL_RESULT_SUCCESS == result);  
  214.   
  215.     /* Enqueue buffers to map the region of memory allocated to store the recorded data */  
  216.     ALOGD("Enqueueing buffer ");  
  217.     for (int i = 0; i < NB_BUFFERS_IN_QUEUE; i++) {  
  218.         ALOGD("%d ", i);  
  219.         result = (*recBuffQueueItf)->Enqueue(recBuffQueueItf, cntxt.pData, BUFFER_SIZE_IN_BYTES);  
  220.         assert(SL_RESULT_SUCCESS == result);  
  221.         cntxt.pData += BUFFER_SIZE_IN_BYTES;  
  222.     }  
  223.     cntxt.pData = cntxt.pDataBase;  
  224.   
  225.     /* Start recording */  
  226.     result = (*recordItf)->SetRecordState(recordItf, SL_RECORDSTATE_RECORDING);  
  227.     assert(SL_RESULT_SUCCESS == result);  
  228.     ALOGD("Starting to record");  
  229.   
  230. }  
  231.   
  232. /* 
  233.  * Class:     com_example_testopensl_AudioRecordActivity 
  234.  * Method:    stop 
  235.  * Signature: ()V 
  236.  */  
  237. JNIEXPORT void JNICALL Java_com_example_testopensl_AudioRecordActivity_stop(JNIEnv *, jobject) {  
  238.     if (recordItf != NULL) {  
  239.         SLresult result = (*recordItf)->SetRecordState(recordItf, SL_RECORDSTATE_STOPPED);  
  240.         assert(SL_RESULT_SUCCESS == result);  
  241.     }  
  242. }  
  243.   
  244. /* 
  245.  * Class:     com_example_testopensl_AudioRecordActivity 
  246.  * Method:    shutdown 
  247.  * Signature: ()V 
  248.  */  
  249. JNIEXPORT void JNICALL Java_com_example_testopensl_AudioRecordActivity_shutdown(JNIEnv *, jobject) {  
  250.   
  251.     //destroy recorder object , and invlidate all associated interfaces  
  252.     if (recorderObject != NULL) {  
  253.         (*recorderObject)->Destroy(recorderObject);  
  254.         recorderObject = NULL;  
  255.         recordItf = NULL;  
  256.         recBuffQueueItf = NULL;  
  257.         configItf = NULL;  
  258.     }  
  259.   
  260.     // destroy engine object, and invalidate all associated interfaces  
  261.     if (engineObject != NULL) {  
  262.         (*engineObject)->Destroy(engineObject);  
  263.         engineObject = NULL;  
  264.         engineEngine = NULL;  
  265.     }  
  266.   
  267.     //colse the file  
  268.     if (gFile != NULL) {  
  269.         fclose(gFile);  
  270.         gFile == NULL;  
  271.     }  
  272. }  
方法说明:
        Java_com_example_testopensl_AudioRecordActivity_createEngine 创建引擎
        Java_com_example_testopensl_AudioRecordActivity_createAudioRecord创建AudioRecorder并开始录音
        Java_com_example_testopensl_AudioRecordActivity_stop 暂停录音
        Java_com_example_testopensl_AudioRecordActivity_shutdown 释放资源
七、创建/jni/Android.mk 编译文件

[plain]view plaincopy
  1. LOCAL_PATH := $(call my-dir)  
  2.   
  3. include $(CLEAR_VARS)  
  4.   
  5. LOCAL_MODULE    := TestAudio  
  6. LOCAL_SRC_FILES := com_example_testopensl_AudioRecordActivity.cpp  
  7. LOCAL_LDLIBS += -llog -lOpenSLES -landroid  
  8.   
  9. include $(BUILD_SHARED_LIBRARY)  
LOCAL_LDLIBS 需要加llog、lOpenSLES、landroid 编译链接库

八、AndroidManifest 配置

录音时记得加上权限

[html]view plaincopy
  1. <uses-permission android:name="android.permission.RECORD_AUDIO" />  
编写完Eclipse 结构图如下:




运行之后界面图如下:



代码编写完毕.点击开始就可以录音

在代码中需要注意的是录音的缓冲区大小,缓存区太小会导致录音失败(每个设备的最小缓冲区是不一样的),太大当音频实时传输会延迟比较大。这时候可以使用Java API获取最小的缓冲区,文中是写死的2*8192,在实际项目中建议使用API获取,下面是Java API

[java]view plaincopy
  1. int sampleRateInHz = 44100;  
  2. int channelConfig = AudioFormat.CHANNEL_OUT_STEREO;  
  3. int audioFormat = AudioFormat.ENCODING_PCM_16BIT;  
  4. int bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat) * 2;  

这个可以通过jni访问java类AudioRecord实现,从而封装代码。