美文网首页
OpenSL ES录制PCM音频

OpenSL ES录制PCM音频

作者: 钉某人 | 来源:发表于2018-05-18 09:27 被阅读0次

native-lib.cpp 录音源代码留作记录

#include <jni.h>
#include <string>
#include "AndroidLog.h"
#include "RecordBuffer.h"
#include "unistd.h"

/**
 * OpenSLES提供的是基于C语言的API,
 * extern "C":C语言中已经有了头文件以及它的库,我们在C++中直接使用的话,就用extern "C",如果按照C++的符号进行修饰的话,
 * 在库中就会找不到该符号
 */
extern "C" {
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
}

/*
 * jni的示例
 * JNIEXPORT: JNI关键字,表示此函数是要被JNI调用的
 * jstring: 表示方法的返回类型
 * JNICALL: JNI关键字,表示此函数是要被jni调用的
 * JAVA :为JNI中标识此方法的来源于java的标识头
 * com_...MainActivity: 是方法所在的泪的包名+类名
 * stringFromJNI :是方法名
 */
extern "C"
JNIEXPORT jstring JNICALL
Java_com_dingmouren_audiovideostudy_MainActivity_stringFromJNI(
        JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++  哎呀 妈呀";
return env->NewStringUTF(hello.c_str());
}

/*
 * OpenSL相关
 */
/*引擎对象接口,这里声明引擎接口对象*/
static SLObjectItf  engineObject = NULL;

/*引擎对象,这里是声明具体的引擎对象实例*/
static SLEngineItf engineEngine;

/*录音器对象接口*/
static SLObjectItf recorderObject = NULL;

/*录音器对象*/
static SLRecordItf recorderRecord;

/*缓冲队列*/
static SLAndroidSimpleBufferQueueItf  recorderBufferQueue;

/*录制大小为4096
 * #define有很多用法,这里是定义了一个简单函数,使用了括号。用法总结:https://blog.csdn.net/ylwdi/article/details/7027384
 * */
#define RECORDER_FRAMES (2048)
static unsigned recorderSize = RECORDER_FRAMES * 2;

/*PCM文件*/
FILE *pcmFile;

/*录音的buffer*/
RecordBuffer *recordBuffer;

bool finished = false;

/*录音的回调
 * fwrite()函数:
 * size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream);
 * buffer:指向数据块的指针
 * size:每个数据的大小,单位是byte,例如sizeof(int)是4
 * count:数据大小,数据个数
 * stream:文件指针
 * */
void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context){
    LOGD("录制大小:%d",recorderSize);
    //以二进制值写入文件,fwrite调用格式不同返回结果也不同,此处成功写入的话返回实际写入的数据大小,单位是byte,也就是recorderSize
    fwrite(recordBuffer->getNowBuffer(),1,recorderSize,pcmFile);

    if(finished){
        (*recorderRecord)->SetRecordState(recorderRecord,SL_RECORDSTATE_STOPPED);
        //刷新缓冲区后,关闭流
        fclose(pcmFile);
        LOGD("停止录音");
    } else{
        (*recorderBufferQueue)->Enqueue(recorderBufferQueue,recordBuffer->getRecordBuffer(),recorderSize);
    }
}
/*开始录音
 * */
extern "C"
JNIEXPORT void JNICALL
Java_com_dingmouren_audiovideostudy_audio_OpenSLActivity_rdSound(JNIEnv *env, jobject instance,
                                                                 jstring path_) {
    const char *path = env->GetStringUTFChars(path_, 0);

    /*PCM文件*/
    pcmFile = fopen(path,"w");

    /*PCMBuffer队列*/
    recordBuffer = new RecordBuffer(RECORDER_FRAMES * 2);

    SLresult  result;

    /*创建引擎对象*/
    result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);//创建引擎接口对象
    result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE); //Realize()函数实现引擎接口对象
    result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);//GetInterface()函数初始化SLEngnineItf对象实例

    /*s设置IO设备(麦克风)*/
    SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
                                      SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};
    SLDataSource audioSrc = {&loc_dev, NULL};

    /*设置buffer队列*/
    SLDataLocator_AndroidSimpleBufferQueue loc_bq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};// 这里因为配置错了出现的错误pAudioSnk: data locator type 0x800007be not allowed

    /*设置录制规格:PCM、2声道、44100Hz、16bit*/
    SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, 2, SL_SAMPLINGRATE_44_1,
                                   SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
                                   SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, SL_BYTEORDER_LITTLEENDIAN};
    SLDataSink audioSnk = {&loc_bq, &format_pcm};

    const SLInterfaceID  id[1] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
    const SLboolean req[1] = {SL_BOOLEAN_TRUE};

    /*创建录制器*/
    result = (*engineEngine)->CreateAudioRecorder(engineEngine,&recorderObject,&audioSrc,&audioSnk,1,id,req);

    if(SL_RESULT_SUCCESS != result) return;

    result = (*recorderObject)->Realize(recorderObject,SL_BOOLEAN_FALSE);

    if(SL_RESULT_SUCCESS != result) return;

    result = (*recorderObject)->GetInterface(recorderObject,SL_IID_RECORD,&recorderRecord);
    result = (*recorderObject)->GetInterface(recorderObject,SL_IID_ANDROIDSIMPLEBUFFERQUEUE,&recorderBufferQueue);

    finished = false;

    result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue,recordBuffer->getRecordBuffer(),recorderSize);
    result = (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue,bqRecorderCallback,NULL);

    LOGD("开始录音");

    /*开始录音*/
    (*recorderRecord)->SetRecordState(recorderRecord,SL_RECORDSTATE_RECORDING);

    env->ReleaseStringUTFChars(path_, path);
}
/*
 * 停止录音
 * */
extern "C"
JNIEXPORT void JNICALL
Java_com_dingmouren_audiovideostudy_audio_OpenSLActivity_rdStop(JNIEnv *env, jobject instance) {

    if(NULL != recorderRecord) finished = true;

}

AndroidLog.h

//日志头文件,
#ifndef WLPLAYER_ANDROIDLOG_H //用于保证同一头文件不被包含多次。如果两个cpp源文件同时include同一个.h头文件,将两个cpp源文件编译成可执行文件时会出现声明冲突
#define WLPLAYER_ANDROIDLOG_H //为宏定义命令
#include <android/log.h>
#define LOGD(FORMAT,...) __android_log_print(ANDROID_LOG_DEBUG,"dingmouren",FORMAT,##__VA_ARGS__)
#define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"dingmouren",FORMAT,##__VA_ARGS_)
#define LOG_DEBUG false
#endif //结束一个#if……#else条件编译块

RecordBuffer.h

#ifndef OPENSLRECORD_RECORDBUFFER_H //防止同一.h头文件被多次包含
#define OPENSLRECORD_RECORDBUFFER_H  //为宏定义命令

class RecordBuffer{
public:
    short **buffer;//只向指针的指针
    int index = -1;
public:
    RecordBuffer(int buffersize);
    ~RecordBuffer();//析构函数,会在每次删除所创建的对象时执行,用于跳出程序前释放资源,比如关闭文件、释放内存等
    /*得到一个新的录制buffer,是即将录入PCM数据的buffer*/
    short* getRecordBuffer();
    /*得到当前录制的buffer,是当前录制好的PCM数据的buffer,可以写入文件,也就是我们得到的PCM数据*/
    short* getNowBuffer();//short*是指将short类型转成指针
};

#endif //结束一个if...else条件编译块

RecordBuffer.cpp

#include "RecordBuffer.h"

/*源文件中定义成员函数*/
RecordBuffer::RecordBuffer(int buffersize) {
    buffer = new short *[2];
    for(int i = 0 ;i < 2;i++){
        buffer[i] = new short[buffersize];
    }
}

RecordBuffer::~RecordBuffer() {

}

short *RecordBuffer::getRecordBuffer() {
    index ++;
    if(index > 1){
        index = 0;
    }
    return buffer[index];
}

short *RecordBuffer::getNowBuffer() {
    return buffer[index];
}

源代码Github

相关文章

网友评论

      本文标题:OpenSL ES录制PCM音频

      本文链接:https://www.haomeiwen.com/subject/pkvwdftx.html