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];
}