OpenSL ES录制PCM音频

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,429评论 25 707
  • 原文链接http://www.cnblogs.com/kenshincui/p/4186022.html 音频在i...
    Hyman0819阅读 21,661评论 4 74
  • 提醒一下,纯个人笔记,你完全可能看晕 一、音频数字化基础知识 见书,列出知识点如下: 声音声波,声音频率、响度, ...
    YY17阅读 31,193评论 6 48
  • 台湾著名散文家林清玄提出:孩子在学校考7至17名,将来更可能成功。他认为现在世界精英都不是当年的尖子生,他们在班级...
    半亩书香阅读 177评论 0 0
  • 为什么别人家的code都是艺术品,而我的。。。就像是。。。屎。。。自己都懒得回头看。。。 --某日程序出bug有感
    J_I_N_G阅读 47评论 0 0