native-lib.cpp 錄音源代碼留作記錄
#include <jni.h>
#include <string>
#include "AndroidLog.h"
#include "RecordBuffer.h"
#include "unistd.h"
/**
* OpenSLES提供的是基于C語言的API,
* extern "C":C語言中已經(jīng)有了頭文件以及它的庫独柑,我們在C++中直接使用的話氓扛,就用extern "C",如果按照C++的符號進行修飾的話,
* 在庫中就會找不到該符號
*/
extern "C" {
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
}
/*
* jni的示例
* JNIEXPORT: JNI關鍵字趋艘,表示此函數(shù)是要被JNI調(diào)用的
* jstring: 表示方法的返回類型
* JNICALL: JNI關鍵字,表示此函數(shù)是要被jni調(diào)用的
* 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有很多用法,這里是定義了一個簡單函數(shù)沐序,使用了括號。用法總結: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;
/*錄音的回調(diào)
* fwrite()函數(shù):
* size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream);
* buffer:指向數(shù)據(jù)塊的指針
* size:每個數(shù)據(jù)的大小忿峻,單位是byte,例如sizeof(int)是4
* count:數(shù)據(jù)大小薄啥,數(shù)據(jù)個數(shù)
* stream:文件指針
* */
void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context){
LOGD("錄制大小:%d",recorderSize);
//以二進制值寫入文件逛尚,fwrite調(diào)用格式不同返回結果也不同垄惧,此處成功寫入的話返回實際寫入的數(shù)據(jù)大小,單位是byte,也就是recorderSize
fwrite(recordBuffer->getNowBuffer(),1,recorderSize,pcmFile);
if(finished){
(*recorderRecord)->SetRecordState(recorderRecord,SL_RECORDSTATE_STOPPED);
//刷新緩沖區(qū)后绰寞,關閉流
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;
/*創(chuàng)建引擎對象*/
result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);//創(chuàng)建引擎接口對象
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE); //Realize()函數(shù)實現(xiàn)引擎接口對象
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);//GetInterface()函數(shù)初始化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};// 這里因為配置錯了出現(xiàn)的錯誤pAudioSnk: data locator type 0x800007be not allowed
/*設置錄制規(guī)格: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};
/*創(chuàng)建錄制器*/
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源文件編譯成可執(zhí)行文件時會出現(xiàn)聲明沖突
#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();//析構函數(shù)铜靶,會在每次刪除所創(chuàng)建的對象時執(zhí)行,用于跳出程序前釋放資源他炊,比如關閉文件争剿、釋放內(nèi)存等
/*得到一個新的錄制buffer,是即將錄入PCM數(shù)據(jù)的buffer*/
short* getRecordBuffer();
/*得到當前錄制的buffer,是當前錄制好的PCM數(shù)據(jù)的buffer,可以寫入文件已艰,也就是我們得到的PCM數(shù)據(jù)*/
short* getNowBuffer();//short*是指將short類型轉成指針
};
#endif //結束一個if...else條件編譯塊
RecordBuffer.cpp
#include "RecordBuffer.h"
/*源文件中定義成員函數(shù)*/
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];
}