环境准备

请按照我之前的文章-Android下基于SDL的位图渲染,安装必要的开发环境。

实践篇

这里主要参考Beginning SDL 2.0(6) 音频渲染及wav播放,只不过将源从WAV文件改成PCM文件。

准备你要播放的PCM文件

如故你不想使用我提供的PCM,可以自己用ffmpeg转一个PCM文件,具体命令如下:

$ ffmpeg -i src.wav -f s16le -acodec pcm_s16le out.pcm

注意你需要知道这个PCM的采样率、量化位数、声道数。后续播放的时候会用到。

生成之后,将pcm文件放到手机的目录下,我使用的是/sdcard/congtou_8k_mono_16bit.pcm

SDL音频播放流程

SDL中音频播放相对简单,只要通过SDL_OpenAudio打开设备,调用SDL_PauseAudio开始播放,播放结束调用SDL_CloseAudio。

这里说明一点,SDL2支持多音频同时播放,不过在打开音频设备的时候需要调用SDL_OpenAudioDevice。

下面是实现的代码:

struct AudioPlayContext
{
bool is_exit; // is audio play buffer empty?
FILE * fpcm;
}; void MyAudioCallback(void* userdata, Uint8* stream, int need_size)
{
AudioPlayContext * context = reinterpret_cast<AudioPlayContext *>(userdata);
size_t actual_read = fread(stream, 1, need_size, context->fpcm);
if (0 == actual_read || feof(context->fpcm))
{
LOGD("we meet stream end %u", actual_read);
context->is_exit = true;
}
} extern "C" int pcm_main(int argc, char *argv[])
{
// init sdl
if (0 != SDL_Init(SDL_INIT_AUDIO))
{
LOGE("%s %d SDL init audio failed", __FUNCTION__, __LINE__);
return -1;
} AudioPlayContext ctx;
ctx.is_exit = false;
ctx.fpcm = NULL; // load yuv
const char *pcm_path = "/sdcard/congtou_8k_mono_16bit.pcm";
LOGI("natvie_SDL load pcm %s", pcm_path);
ctx.fpcm = fopen(pcm_path, "rb");
if (NULL == ctx.fpcm)
{
LOGE("%s %d load pcm failed path %s", __FUNCTION__, __LINE__, pcm_path);
SDL_Quit();
return -2;
} // open audio device
SDL_AudioSpec want, have;
SDL_memset(&want, 0, sizeof(want)); /* or SDL_zero(want) */
SDL_memset(&want, 0, sizeof(have)); /* or SDL_zero(want) */
want.freq = 8000;
want.format = AUDIO_S16LSB;
want.channels = 1;
want.samples = 1024;
want.callback = MyAudioCallback; // you wrote this function elsewhere.
want.userdata = &ctx; if (SDL_OpenAudio(&want, &have) < 0)
{
LOGE("Failed to open audio: %s", SDL_GetError());
SDL_Quit();
return -3;
} if (have.format != want.format)
{
LOGD("We didn't get AUDIO_S16LSB audio format want %d have %d", want.format, have.format);
} // start audio playing.
SDL_PauseAudio(0); // wait for the end
while (!ctx.is_exit)
{
SDL_Delay(1000);
} SDL_CloseAudio();
fclose(ctx.fpcm); // Quit SDL
SDL_Quit(); return 0;
}

音频播放的处理主要是按照音频设备回调函数填充需要播放的PCM的数据,只要数据填充正确基本就没有什么问题。

理论篇

如果你之前看过SDLActivity.java的实现代码,会发现其中有四个函数:

public static int audioInit(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames)
public static void audioWriteShortBuffer(short[] buffer)
public static void audioWriteByteBuffer(byte[] buffer)
public static void audioQuit()

还有一个变量,protected static AudioTrack mAudioTrack

很明显的,SDL2通过JNI调用,将C层的PCM数据通过audioWriteShortBuffer/audioWriteByteBuffer调用到java层,并将PCM数据交给AudioTrack播放(至于AudioTrack怎么用,建议参考Android开发文档)。

那么看一下SDL内部是怎么实现的?

SDL_OpenAudioDevice --> open_audio_device --> AndroidAUD_CloseDevice --> Android_JNI_OpenAudioDevice

最核心的函数是open_audio_device,在这个函数里会检查输入输出的音频参数(采样率、采样位数、声道数等),打开音频输出设备,同时启动音频输出线程,定期回调用户注册的回调函数,并将数据写入到音频设备中。有兴趣的建议看看SDL2的源码,关于音频的处理集中在SDL2-src/src/audio目录下。

附加说明

源码下载

本文中涉及所有源码可以从我的git@OSC,下载之后需要切换到pcm_render的tag即可。

其他

本部分主要介绍如何在Android下使用SDL播放PCM数据,最终音频播放是通过AudioTrack实现的。

Android下基于PCM的音频渲染的更多相关文章

  1. Android下基于SDL的YUV渲染

    实战篇 本文主要参考我之前整理的文章windows下使用SDL进行YUV渲染. 相对于之前写的位图渲染部分(http://www.cnblogs.com/tocy/p/android-sdl-bitm ...

  2. Android下基于SDL的位图渲染(二)理论篇

    理论篇 上一篇中介绍了如何将SDL2源码应用到Android渲染中,实际上SDL本身提供的android-project实现了基于android的c运行时环境,通过上面实践篇的介绍,就是完成这个环境搭 ...

  3. Android下基于SDL的位图渲染(一)

    环境准备 安装Android开发环境(java.android-sdk.android ndk.gcc). 我使用的ndk版本是r10b/r10d,在win10/ubutu 15.04编译 下载sdl ...

  4. Android下基于线程池的网络访问基础框架

    引言 现在的Android开发很多都使用Volley.OkHttp.Retrofit等框架,这些框架固然有优秀的地方(以后会写代码学习分享),但是我们今天介绍一种基于Java线程池的网络访问框架. 实 ...

  5. 基于FFmpeg的音频编码(PCM数据编码成AAC android)

    概述 在Android上实现录音,并利用 FFmpeg将PCM数据编码成AAC. 详细 代码下载:http://www.demodashi.com/demo/10512.html 之前做的一个demo ...

  6. Android 音视频开发(一):PCM 格式音频的播放与采集

    什么是 PCM 格式 声音从模拟信号转化为数字信号的技术,经过采样.量化.编码三个过程将模拟信号数字化. 采样 顾名思义,对模拟信号采集样本,该过程是从时间上对信号进行数字化,例如每秒采集 44100 ...

  7. 最简单的基于FFMPEG的音频编码器(PCM编码为AAC)

    http://blog.csdn.net/leixiaohua1020/article/details/25430449 本文介绍一个最简单的基于FFMPEG的音频编码器.该编码器实现了PCM音频采样 ...

  8. 音频处理之去噪算法---基于pcm和g711的音频16000hz、8bit去噪声算法

    (1)应用背景 (2)主要降噪算法原理 (3)算法流程 (4)算法实现 (5) ------------author:pkf -------------------time:2-6 --------- ...

  9. Dalvik模式下基于Android运行时类加载的函数dexFindClass脱壳

    本文博客地址:http://blog.csdn.net/qq1084283172/article/details/78003184 前段时间在看雪论坛发现了<发现一个安卓万能脱壳方法>这篇 ...

随机推荐

  1. 【CI】CN.一种多尺度协同变异的微粒群优化算法

    [论文标题]一种多尺度协同变异的微粒群优化算法 (2010) [论文作者]陶新民,刘福荣, 刘  玉 , 童智靖 [论文链接]Paper(14-pages // Single column) [摘要] ...

  2. “医疗信息化行业之中的联发科”- 我们在医疗行业中的定位及目标 想做一个面对中小企业的专业上游软件供应商 台湾联发科技颠覆掉的是一个封闭的手机产业系统 解决方案,即AgileHIS.NET数字化医院基础方案

    “医疗信息化行业之中的联发科”- 我们在医疗行业中的定位及目标   我们做中国医疗信息化行业之中的联发科 ---我们在医疗行业中的定位及目标 从我个人来讲,我从2001年到现在这10年之间基本上一直在 ...

  3. C语言学习笔记 (008) - C语言字符串操作总结大全(超详细)(转)

    1)字符串操作 strcpy(p, p1) 复制字符串 strncpy(p, p1, n) 复制指定长度字符串 strcat(p, p1) 附加字符串 strncat(p, p1, n) 附加指定长度 ...

  4. 【IL】IL指令详解

    名称 说明 Add 将两个值相加并将结果推送到计算堆栈上. Add.Ovf 将两个整数相加,执行溢出检查,并且将结果推送到计算堆栈上. Add.Ovf.Un 将两个无符号整数值相加,执行溢出检查,并且 ...

  5. 图片标注工具LabelImg使用教程

    1.进入labelImg-master文件夹,在空白处使用 “Shift+鼠标右键” ,选择在此处打开命令窗口,依次输入下面语句即可打开软件. pyrcc4 -o resources.py resou ...

  6. 【struts2】Action的生命周期

    Struts2的Action的生命周期是:Struts2为每个请求都重新初始化一个Action的实例.可以稍微改造一下代码来验证一下. 给HelloWorldAction加上一个public无参的构造 ...

  7. 《JAVA与模式》之简单工厂与工厂方法

    一.简单工厂 1.1 使用场景 1.工厂类负责创建的对象比较少: 2.客户只知道传入工厂类的参数,对于如何创建对象(逻辑)不关心: 3.由于简单工厂很容易违反高内聚责任分配原则,因此一般只在很简单的情 ...

  8. Easyui combobox 始终选择第一个的问题

    //必须指定 id 和 text $('#contact_city').combobox({ valueField:'id', textField:'text', });

  9. xcode修改项目名后反复出现 clang error

    xcode修改项目名后反复出现 clang error,  提示 ld: file not found . 并且该错误并不是出现在项目编译阶段,而是项目的Tests 的link阶段, 同时提示 xct ...

  10. PowerDsigner 16逆向工程导入mysql

    由于日常数据建模经常使用PowerDesigner,使用逆向工程能更加快速的生成模型提高效率,所以总结使用如下: 1.      安装MYSQL的ODBC驱动 Connector/ODBC 5.1.1 ...