您当前的位置: 首页 > 学无止境 > 心得笔记 网站首页心得笔记
5. 读取pcm音频文件并使用OpenSL播放~1
发布时间:2021-06-04 22:13:10编辑:雪饮阅读()
上篇完成了混音器、配置了音頻信息。這次我們就能正式的進行pcm音頻的播放了。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.audio_opensles">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Audio_opensles">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
那麽部署運行在如雷電4中如:
#include <string>
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include <android/log.h>
#define LOGD(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"ywl5320",FORMAT,##__VA_ARGS__);
/*
* SLObjectItf:OpenSL ES中最重要的接口類
* */
static SLObjectItf engineSL = NULL;
/*
* SLEngineItf:OpenSL ES中具体的引擎(接口類)
* */
SLEngineItf CreateSL()
{
/*SLresult:返回結果
* 下面三個方法:
* slCreateEngine、Realize、GetInterface
* 返回結果都是SLresult類型,所以這裏統一定義一個返回結果類型
* */
SLresult re;
SLEngineItf en;
/*
* slCreateEngine方法用於创建一个引擎接口对象,這裏一般只關注第一個參數,其餘5個參數一般設置為0即可
* */
re = slCreateEngine(&engineSL,0,0,0,0,0);
if(re != SL_RESULT_SUCCESS) return NULL;
//创建好引擎接口对象后,需要用SLObjectItf的Realize方法来实现engineObject,Realize方法的第二個參數async表示是否異步,這裏使用同步
re = (*engineSL)->Realize(engineSL,SL_BOOLEAN_FALSE);
if(re != SL_RESULT_SUCCESS) return NULL;
//GetInterface方法来初始化SLEngnineItf对象实例,GetInterface的第二個參數是一個SL接口id,這裏一般用常量SL_IID_ENGINE
re = (*engineSL)->GetInterface(engineSL,SL_IID_ENGINE,&en);
if(re != SL_RESULT_SUCCESS) return NULL;
return en;
}
void PcmCall(SLAndroidSimpleBufferQueueItf bf,void *contex)
{
LOGD("PcmCall");
static FILE *fp = NULL;
static char *buf = NULL;
if(!buf)
{
//buf,每次讀取的内容之後存到buf中,然後把buf扔到隊列中
buf = new char[1024*1024];
}
if(!fp)
{
fp = fopen("/sdcard/test.pcm","rb");
}
if(!fp)return;
//文件未結束feof返回0
if(feof(fp) == 0)
{
/*
*參數1:指向要读取的数组中首个对象的指针
*參數2:每个对象的大小(单位是字节),這裏假定我們讓它每次讀取的比較小的對象大小
*參數3:要读取的对象个数,這裏假定我們讀取1024個對象
* 參數4:輸入流
* */
int len = fread(buf,1,1024,fp);
if(len > 0){
//只有本次有讀取到東西才能放入到隊列中
(*bf)->Enqueue(bf,buf,len);
}
}
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_audio_1opensles_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
//1 创建引擎
SLEngineItf eng = CreateSL();
if(eng)
{
LOGD("CreateSL success! ");
}
else
{
LOGD("CreateSL failed! ");
}
//2 创建混音器
SLObjectItf mix = NULL;
SLresult re = 0;
//CreateOutputMix函數用於創建輸出混音器
re = (*eng)->CreateOutputMix(eng,&mix,0,0,0);
if(re !=SL_RESULT_SUCCESS )
{
LOGD("SL_RESULT_SUCCESS failed!");
}
re = (*mix)->Realize(mix,SL_BOOLEAN_FALSE);
if(re !=SL_RESULT_SUCCESS )
{
LOGD("(*mix)->Realize failed!");
}
//配置混音器 : 将 mix 混音器对象装载入 SLDataLocator_OutputMix 结构体中
SLDataLocator_OutputMix outmix = {SL_DATALOCATOR_OUTPUTMIX,mix};
//// 数据定位器是一个输出混合:将 SLDataLocator_OutputMix 结构体装载到 SLDataSink 中
SLDataSink audioSink= {&outmix,0};
//3 配置音频信息
//缓冲队列
SLDataLocator_AndroidSimpleBufferQueue que = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,10};
//音频格式
SLDataFormat_PCM 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 //字节序,小端
};
// 数据源是含有PCM格式的简单缓冲区队列
SLDataSource ds = {&que,&pcm};
//4 创建播放器
SLObjectItf player = NULL;
//OpenSL ES中具体的接口类 播放器:SLPlayItf
SLPlayItf iplayer = NULL;
//播放队列
SLAndroidSimpleBufferQueueItf pcmQue = NULL;
//接口id 需要的接口SL_IID_BUFFERQUEUE
const SLInterfaceID ids[] = {SL_IID_BUFFERQUEUE};
//需要的接口,如果所需要的接口不要用,请求将失败
const SLboolean req[] = {SL_BOOLEAN_TRUE};
// 创建音频播放器对象,第五個參數是計算所需要的接口數量
re = (*eng)->CreateAudioPlayer(eng,&player,&ds,&audioSink,sizeof(ids)/sizeof(SLInterfaceID),ids,req);
if(re !=SL_RESULT_SUCCESS )
{
LOGD("CreateAudioPlayer failed!");
} else{
LOGD("CreateAudioPlayer success!");
}
(*player)->Realize(player,SL_BOOLEAN_FALSE);
//获取player接口
re = (*player)->GetInterface(player,SL_IID_PLAY,&iplayer);
if(re !=SL_RESULT_SUCCESS )
{
LOGD("GetInterface SL_IID_PLAY failed!");
}
re = (*player)->GetInterface(player,SL_IID_BUFFERQUEUE,&pcmQue);
if(re !=SL_RESULT_SUCCESS )
{
LOGD("GetInterface SL_IID_BUFFERQUEUE failed!");
}
//设置回调函数,播放队列空调用
(*pcmQue)->RegisterCallback(pcmQue,PcmCall,0);
//设置为播放状态
(*iplayer)->SetPlayState(iplayer,SL_PLAYSTATE_PLAYING);
/*
*启动队列回调 第二個參數是要向隊列傳遞一個值,因爲上面RegisterCallback是隊列為空才調用,但是第一次執行也是空的,爲了讓第一次不被調用,就傳一個空字符即可
* 最後一個參數是第二個參數的大小,這裏正好是1個字節
* */
(*pcmQue)->Enqueue(pcmQue,"",1);
return env->NewStringUTF(hello.c_str());
}
关键字词:OpenSL,pcm