native-lib.cpp

#include <jni.h>
#include <string> #include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h> #include <android/log.h> #define LOGD(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"xp.chen",FORMAT,##__VA_ARGS__); void bufferQueueCallback(SLAndroidSimpleBufferQueueItf bufferQueue, void *pContext) {
static FILE *fp = NULL;
static char *buf = NULL;
if (!buf) {
buf = new char[*];
}
if (!fp) {
fp = fopen("/sdcard/test.pcm", "rb");
}
if (!fp) return;
if (feof(fp) == ) {
int len = fread(buf, , , fp);
if (len > )
(*bufferQueue)->Enqueue(bufferQueue, buf, len);
}
} extern "C" JNIEXPORT jstring JNICALL
Java_com_yongdaimi_android_androidapitest_OpenSLESApiUseDemoActivity_stringFromJNI(JNIEnv* env, jobject object)
{
std::string hello = "Hello from C++"; SLresult re;
SLObjectItf engineObject;
SLEngineItf slAudioEngine; // 1. Create and init audio engine
re = slCreateEngine(&engineObject, , NULL, , NULL, NULL);
if (re != SL_RESULT_SUCCESS) {
LOGD("slCreateEngine() failed");
}
re = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
if (re != SL_RESULT_SUCCESS) {
LOGD("engineObject Realize failed");
}
re = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &slAudioEngine);
if (re != SL_RESULT_SUCCESS) {
LOGD("engineObject GetInterface SL_IID_ENGINE failed");
} // 2. Set output mix
SLObjectItf outputMix;
re = (*slAudioEngine)->CreateOutputMix(slAudioEngine, &outputMix, , NULL, NULL);
if (re != SL_RESULT_SUCCESS) {
LOGD("CreateOutputMix() failed");
}
re = (*outputMix)->Realize(outputMix, SL_BOOLEAN_FALSE);
if (re != SL_RESULT_SUCCESS) {
LOGD("outputMix Realize failed");
} // 3. Configuring the input data source
SLDataLocator_AndroidSimpleBufferQueue inputBuffQueueLocator = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, };
SLDataFormat_PCM input_format_pcm = {
SL_DATAFORMAT_PCM, // <<< 输入的音频格式,PCM
, // <<< 输入的声道数,2(立体声)
SL_SAMPLINGRATE_44_1, // <<< 输入的采样率,44100hz
SL_PCMSAMPLEFORMAT_FIXED_16, // <<< 输入的采样位数,16bit
SL_PCMSAMPLEFORMAT_FIXED_16, // <<< 容器大小,同上
SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT, // <<< 声道标记,这里使用左前声道和右前声道
SL_BYTEORDER_LITTLEENDIAN // <<< 输入的字节序,小端
};
SLDataSource dataSource = {&inputBuffQueueLocator, &input_format_pcm}; SLDataLocator_OutputMix outputMixLocator = {SL_DATALOCATOR_OUTPUTMIX, outputMix};
SLDataSink dataSink = {&outputMixLocator, }; // 4. Create Audio Player
SLObjectItf audioPlayer;
SLAndroidSimpleBufferQueueItf pcmBufferQueue;
SLPlayItf playInterface;
SLInterfaceID audioPlayerInterfaceIDs[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
SLboolean audioPlayerInterfaceRequired[] = {SL_BOOLEAN_TRUE}; re = (*slAudioEngine)->CreateAudioPlayer(slAudioEngine, &audioPlayer, &dataSource, &dataSink, , audioPlayerInterfaceIDs, audioPlayerInterfaceRequired);
if (re != SL_RESULT_SUCCESS) {
LOGD("CreateAudioPlayer() failed");
}
re = (*audioPlayer)->Realize(audioPlayer, SL_BOOLEAN_FALSE);
if (re != SL_RESULT_SUCCESS) {
LOGD("AudioPlayer Realize failed");
}
re = (*audioPlayer)->GetInterface(audioPlayer, SL_IID_PLAY, &playInterface);
if (re != SL_RESULT_SUCCESS) {
LOGD("AudioPlayer GetInterface SL_IID_PLAY failed");
}
re = (*audioPlayer)->GetInterface(audioPlayer, SL_IID_BUFFERQUEUE, &pcmBufferQueue);
if (re != SL_RESULT_SUCCESS) {
LOGD("AudioPlayer GetInterface SL_IID_BUFFERQUEUE failed");
} (*pcmBufferQueue)->RegisterCallback(pcmBufferQueue, bufferQueueCallback, NULL);
(*playInterface)->SetPlayState(playInterface, SL_PLAYSTATE_PLAYING); // Start queue callback
(*pcmBufferQueue)->Enqueue(pcmBufferQueue, "", );
return env->NewStringUTF(hello.c_str());
}

代码中的一些概念补充:

1.结构体中的 numInterfaces , pInterfaceIds , pInterfaceRequired ,这里以创建播放器所调用的 CreateAudioPlayer 函数为例说明:

SLresult (*CreateAudioPlayer) (
SLEngineItf self,
SLObjectItf * pPlayer,
SLDataSource *pAudioSrc,
SLDataSink *pAudioSnk,
SLuint32 numInterfaces,
const SLInterfaceID * pInterfaceIds,
const SLboolean * pInterfaceRequired
);

各参数含义如下:

  • SLEngineItf C语言不像C++,没有this指针,只能通过每次调用SLEngineItf的方法的时候手动传入
  • SLObjectItf 用于保存创建出来的AudioPlayerObject
  • SLDataSource 输入数据源的信息
  • SLDataSink 输出的信息
  • numInterfaces 与下面的SLInterfaceID和SLboolean配合使用,用于标记SLInterfaceID数组和SLboolean数组的大小
  • SLInterfaceID 这里需要传入一个数组,指定创建的AudioPlayerObject需要包含哪些Interface
  • SLboolean 这里也是一个数组,用来标记每个需要包含的Interface在AudioPlayerObject不支持的情况下,是不是需要在创建AudioPlayerObject时返回失败。

最后的三个参数用于指定AudioPlayerObject需要包含哪些Interface,如果不包含是不是要直接创建失败。之前也提到过,并不是每个系统上都实现了 OpenSL ES 为 Object 定义的所有 Interface,所以在获取 Interface 的时候需要做一些选择和判断,如果创建成功的话我们就能使用AudioPlayerObject的GetInterface方法获取到这些Interface了。

2. DataSouce和DataSink

OpenSL ES 里面,这两个结构体均是作为创建 Media Object 对象时的参数而存在的,data source 代表着输入源的信息,即数据从哪儿来、输入的数据参数是怎样的;而 data sink 则代表着输出的信息,即数据输出到哪儿、以什么样的参数来输出。

OpenSL ES: 利用OpenSL ES播放一个存在于SDcard上的PCM文件的更多相关文章

  1. Android OpenSL ES 开发:OpenSL ES利用SoundTouch实现PCM音频的变速和变调

    缘由 OpenSL ES 学习到现在已经知道 OpenSL ES 不仅能播放和录制PCM音频数据,还能改变声音大小.设置左声道或右声道播放.还能变速播放,可谓是播放音频的王者.但是变速有一点不好的就是 ...

  2. ES 记录之如何创建一个索引映射,以及一些设置

    ElasticSearch 系列文章 1 ES 入门之一 安装ElasticSearcha 2 ES 记录之如何创建一个索引映射 3 ElasticSearch 学习记录之Text keyword 两 ...

  3. 利用videojs自动播放下一个

    利用videojs自动播放下一个 一.总结 一句话总结: 在视频放完的ended方法里面,指定video的src,然后this.play()放视频就好 vue来控制视频的链接也是蛮不错的 this.o ...

  4. OpenGL ES: (1) OpenGL ES的由来 (转)

    1. 电脑是做什么用的? 电脑又被称为计算机,那么最重要的工作就是计算.看过三体的同学都知道, 电脑中有无数纳米级别的计算单元,通过 0 和 1 的转换,完成加减乘除的操作. 2. 是什么使电脑工作? ...

  5. es笔记---新建es索引

    es对索引的一堆操作都是用restful api去进行的,参数时一堆json,一年前边查边写搞过一次,这回搞迁移,发现es都到6.0版本了,也变化了很多,写个小笔记记录一下. 创建一个es索引很简单, ...

  6. [OpenGL ES 02]OpenGL ES渲染管线与着色器

    [OpenGL ES 02]OpenGL ES渲染管线与着色器 罗朝辉 (http://www.cnblogs.com/kesalin/) 本文遵循"署名-非商业用途-保持一致"创 ...

  7. 利用SCI做的一个足球答题系统

    SCI,异步串行通信接口,内置独立的波特率产生电路和SCI收发器,可以选择发送8或9个数据位(其中一位可以指定为奇或偶校验位). SCI是全双工异步串行通信接口,主要用于MCU与其他计算机或设备之间的 ...

  8. iOS Dev (20) 用 AVAudioPlayer 播放一个本地音频文件

    iOS Dev (20) 用 AVAudioPlayer 播放一个本地音频文件 作者:CSDN 大锐哥 博客:http://blog.csdn.net/prevention 步骤 第一步:在 Proj ...

  9. iOS Dev (21) 用 AVPlayer 播放一个本地音频文件

    iOS Dev (21) 用 AVPlayer 播放一个本地音频文件 作者:CSDN 大锐哥 博客:http://blog.csdn.net/prevention 前言 这篇文章与上一篇极其相似,要注 ...

随机推荐

  1. Android笔记(二十) Activity中的跳转和值传递

    我们知道,一个APP是由若干个Activity组成的,那么各个Acitivity中肯定需要进行跳转以及传递数值以保证App的运行,现总结一下多个Activity之间的跳转和值传递. 显式Intent跳 ...

  2. java - day017 - 线程

    进程 操作系统中, 并行执行的任务 线程 进程内部, 并行执行的任务 进程包含线程 线程的创建 继承Thred 定义 Thred 的子类 重写 run() 方法 在 run() 方法中的代码, 是与其 ...

  3. Python_字符串相关操作

    1.字符串切片操作: str1='hello word' startIndex=0 #开始索引位置 endIndex=5 #结束索引位置+1 step=2 #步长 print(str1[startIn ...

  4. LInux CentOS7 vsftpd 配置注释

    本文首发: https://www.somata.work/2019/LinuxCentOSvsftpdConfigComment.html vsftpd.conf anonymous_enable= ...

  5. Vim使用技巧(5) -- 宏的录制与使用

    想象一个场景,我们怎么快速把下面的所有链接都加上双引号?可能你手速快,可以很快的加完,但是如果链接有上万个呢?你如何在十秒以内加完? 这时候就需要用到“宏”(其实除了宏vim还有其它方法加上双引号,这 ...

  6. 【迭代博弈+搜索+剪枝】poj-1568--Find the Winning Move

    poj  1568:Find the Winning Move   [迭代博弈+搜索+剪枝] 题面省略... Input The input contains one or more test cas ...

  7. P2161 [SHOI2009]会场预约[线段树/树状数组+二分/STL]

    题目描述 PP大厦有一间空的礼堂,可以为企业或者单位提供会议场地.这些会议中的大多数都需要连续几天的时间(个别的可能只需要一天),不过场地只有一个,所以不同的会议的时间申请不能够冲突.也就是说,前一个 ...

  8. 优雅的处理vue注册全局组件

    使用情景: 有频繁使用的组件 需要进行全局注册 可以将这些组件都放在components下同一个文件夹下,在此文件夹中建立 一个js文件 代码如下: import Vue from 'vue'; // ...

  9. VSCode - Beautify 插件配置

    1. 在工作目录下建立.jsbeautifyrc文件 官方文档 { "brace_style": "none,preserve-inline", "i ...

  10. 文件读写(三)利用FileStream类操作字节数组byte[]、BinaryFormatter、内存流MemoryStream

    一.Stream类概述 在.NET Framework中,文件和流是有区别的.文件是存储在磁盘上的数据集,它具有名称和相应的路径.当打开一个文件并对其进行读/写时,该文件就称为流(stream).但是 ...