zoukankan      html  css  js  c++  java
  • Android 音频 OpenSL ES 录音 采集

    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

    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

    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 文件

    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

    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 ", 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 ", 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 编译文件

    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 配置

    录音时记得加上权限

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




    运行之后界面图如下:



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

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

    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实现,从而封装代码。

  • 相关阅读:
    json-c初探(一)
    Java程序员跳槽的首选面试题最新合集(2021下半年),初中高级程序员!
    R语言版本的bedtools--bedtoolsr
    使用R语言(cpm包)进行序列变点(change point)检测
    三款PHP大马,已解密、去后门
    php 取出数据表数据放入数组并排序
    VimTutor每讲小结
    记录一下c++学习过程
    vmware fusion关闭自动挂起(suspend)的方法
    mac中安装mysqlclient出错error: command 'clang' failed with exit status 1的解决办法
  • 原文地址:https://www.cnblogs.com/elesos/p/6971608.html
Copyright © 2011-2022 走看看