Android音频: 怎样使用AudioTrack播放一个WAV格式文件?
翻译 By Long Luo
原文链接:Android Audio:
Play a WAV file on an AudioTrack
译者注:
1. 因为这是技术文章,所以有些词句使用原文,表达更准确。
2. 因为水平有效,有些地方可能翻译的不够准确,如有不当之处,敬请批评指正.
3. 针对某些语句。适当补充了上下文及更适合中文阅读,尽量做到信达雅。
假设你已经成功地了解了关于AudioTrack的一些话题。那么你可能享受它带来的优点,比如低延迟(在STATIC(静态)模式),可以生成流式音频(在STREAM(流)模式)以及在播放之前。就行訪问和改动原始声音数据。
只是,如今的问题是怎样从源获取数据。很多应用须要使用的AudioTrack并不能简单的生成PCM音频(一个样例,比方Ethereal
Dialpad或者其它类似的App)。你可能须要从文件源去载入数据。比如WAV或MP3文件。
不要期望使用MediaPlayer。去解码WAV文件和MP3音频。尽管MediaPlayer播放这些文件很好,可是其播放逻辑全然在Native层,同一时候并没有为我们提供额外选项。同意我们使用其它解码器实现我们的目的。因此,我们必须从手动地从音频文件进行解码出PCM。
在这篇文章中,将会讨论WAV格式文件。
而在下一课中。我们将会更进一步。讨论怎样从MP3文件读取音频。
背景知识: 一些数字音频术语
假设你的App不是专门为数字音频设计。那么在继续我们的讨论之前,你可能须要先了解一些主要的缩略语。
别操心。都非常easy。我们不须要对此做深入挖掘。
PCM(脉冲调制方式) – 实现一个物理音频信号变成数字化最简单方法。基本原理就是信号变成了一个数字阵列。而当中每一个数字代表的是声音在特定的时间瞬间的电平也能够说是能量(振幅)。(假设这样的解释在科学上可能不会非常准确。那我就仅仅能说声抱歉了)。
信不信由你。你能够使用这样的方法表示不论什么复杂的声音。并且回放出来也非常精准。
在这里。我们将仅仅会谈到线性PCM。在线性PCM中,当中阵列中的每一个数字都是原始声音振幅的线性表示。在某些情况下,对数映射能够更好地表示原来的声音幅度比例情况
– 可是我们不会讨论那些情况。Sampling rate(採样率):- 每秒你的数字声音有多少样本(声音幅度用数字表示)。样本越多,你能得到声音质量越好。眼下在消费类音频系统眼下使用的採样率一般是22050,44100和48000Hz/s。
每一个样品分辨率/採样大小/位 – 定义表示振幅数字的大小和格式。
比如,假设您使用的是8位整数,你仅仅能表达出256级的幅度,所以原来的物理波形将被简化为256个离散电平。与此同一时候,你将失去一些声音精度也能够说是质量。假设你使用16位。那么声音质量变得更好。其实。大部分时间你可能会使用16位音频。其它选项包含24位。32位(这些都是Android如今不支持的),或是使用浮点数。
声道 – 既能够是单声道,也能够是立体声(2个声道)。或者很多其它声道(可是Android不支持)。
假设你想要有立体声,你须要有立体声音频,就必须要在每一个声道都须要有一个独立的PCM数组。对应的信息量也会翻倍。
上述定义也有助于你理解特定的格式和长度的音频缓冲区的数据量,以便提前预备缓冲区。也就是你须要一个缓冲区。以用于存储5秒长度以44100Hz採样率的立体声16-bit线性PCM数据。数据计算公式例如以下所看到的:
5 sec * 44100 samples
per sec * 2 bytes per sample * 2 channels = 882,000 bytes
这一数额所需的内存可能会让刚開始学习的人感到吃惊,由于当你往你的磁盘上存储的音频时。一个MP3文件,一个880KB的文件就能够容纳以同样的採样率和分辨率1分钟时长的音轨。
这是为什么呢?由于先进的格式,比方MP3格式。由于我们大脑无法分辨识别出一些音频的内容,所以使用了非常多复杂的方式在压缩的过程中去掉了这些内容。
然而,大多数低等级的音频API,包含Android的AudioTrack仅仅能接受线性PCM。这就是为什么假设我们不能把整个样品都放在内存中,我们须要将要处理的数据流,循环缓冲区和其它聪明的方式来使用音频API。
希望这种解释并没有让你产生困惑,如今让我们继续来实际做一些与Android上的数字音频有关的工作吧!
WAV文件格式
我们的目标是用一个InputStream,由其从一个WAV文件载入PCM数据,来提供原始字节数据。然后我们就能够将原始的PCM数据直接推送到使用已经正确的配置好了的AudioTrack.write。通过使用AudioTrack.write()这个API。
WAV文件包括一个文件头和详细数据会。
我们须要读取文件头以知道诸如採样速率。分辨率等信息。另外。我们通过文件头,也能够知道此格式是否支持。WAV能够封装成多种格式,我们无法所有支持。
或许。仅仅是合理的採样率,分辨率和通道的线性PCM格式。
WAV格式的细节在互联网上都能够找到,你只须要在Google上搜索下。可是。遗憾的是。我并没有搜索到一个非常好的Java库来读取WAV文件,并且能够移植到Android下。因此。我自己写了一些简单的代码。
以下这种方法就是怎样读取一个WAV文件的头部:
private static final String RIFF_HEADER = "RIFF";
private static final String WAVE_HEADER = "WAVE";
private static final String FMT_HEADER = "fmt ";
private static final String DATA_HEADER = "data";
private static final int HEADER_SIZE = 44;
private static final String CHARSET = "ASCII";
/* ... */
public static WavInfo readHeader(InputStream wavStream) throws IOException,
DecoderException {
ByteBuffer buffer = ByteBuffer.allocate(HEADER_SIZE);
buffer.order(ByteOrder.LITTLE_ENDIAN);
wavStream.read(buffer.array(), buffer.arrayOffset(), buffer.capacity());
buffer.rewind();
buffer.position(buffer.position() + 20);
int format = buffer.getShort();
checkFormat(format == 1, "Unsupported encoding: " + format); // 1 means
// Linear
// PCM
int channels = buffer.getShort();
checkFormat(channels == 1 || channels == 2, "Unsupported channels: "
+ channels);
int rate = buffer.getInt();
checkFormat(rate <= 48000 && rate >= 11025, "Unsupported rate: " + rate);
buffer.position(buffer.position() + 6);
int bits = buffer.getShort();
checkFormat(bits == 16, "Unsupported bits: " + bits);
int dataSize = 0;
while (buffer.getInt() != 0x61746164) { // "data" marker
Log.d(TAG, "Skipping non-data chunk");
int size = buffer.getInt();
wavStream.skip(size);
buffer.rewind();
wavStream.read(buffer.array(), buffer.arrayOffset(), 8);
buffer.rewind();
}
dataSize = buffer.getInt();
checkFormat(dataSize > 0, "wrong datasize: " + dataSize);
return new WavInfo(new FormatSpec(rate, channels == 2), dataSize);
}
上面的代码中,缺少的部分应该是显而易见的。正如你所示,只支持16位,但在你能够改动代码以支持8位(AudioTrack不支持不论什么其它分辨率的)。
以下这种方法,则是用来读取文件剩余的部分 – 音频数据。
public static byte[] readWavPcm(WavInfo info, InputStream stream)
throws IOException {
byte[] data = new byte[info.getDataSize()];
stream.read(data, 0, data.length);
return data;
}
我们读取的WavInfo结构体,包括採样率,分辨率和声道数已经足够让我们去播放我们读取的音频了。
假设我们不须要将所有音频数据一次性放入内存中,我们能够使用一个InputStream,一点一点地读取。
将PCM传入AudioTrack
我们如今面临2种情况,新建一个适合这样的格式的AudioTrack,或者使用一个已存在的AudioTrack。可是可能和我们WAV音频数据的格式不一致。
在第一种情况,事情就非常easy了,我们只须要使用AudioTrack构造器构造一个我们已经从WAV头部相应的就可以。
另外一种情况,我们就须要将我们的音频变成AudioTrack须要的目标格式。
我们须要做一下几种转换方式:
假设採样率不同,要么丢弃或复制一个样本以便和目标速率相匹配。
假设分辨率是不同的。将源信号分辨率映射到目标分辨率,从16位到8位,反之亦然。
假设信道不同。我们要么将立体声声道混合成一个单声道或反复单声道的数据把它变成准立体声。(请考虑将这些算法的实现放在Native层,由于Native层在做这类处理有非常大的优势。)
在其它情况下。我们已经确定格式已经匹配。我们使用AudioTrack.write()写入缓冲区,以便实现回放。
记住,假设你使用静态模式,你须要在play()之前,新建一个包括准确的缓冲区大小的AudioTrack ,同一时候写入write()音频数据。而在流模式下,我们能够先使用AudioTrack的play(),然后在使用write()写入数据部分
总结
你想实现AudioTrack上播放WAV音频可能有非常多原因。有时候。可能是SoundPool有尺寸限制,或是MediaPlayer会有延迟和对资源占用太高,让你考虑使用这样的方式。有时候你须要改动音频或者混合音频。无论不论什么情况,这篇文章试图告诉你应该怎样做。
在下一篇中,我们将会讨论MP3音频,敬请期待:-)
Long Luo for Part 1 created at 23:15 ~ 00: 33 June 21th, 2014 @Shenzhen, China.
Long Luo for Part 2 created at 16:00 ~ 17: 15 June 22th, 2014 @Shenzhen, China.
Android音频: 怎样使用AudioTrack播放一个WAV格式文件?的更多相关文章
- 将PCM格式存储成WAV格式文件
将PCM格式存储成WAV格式文件 WAV比PCM多44个字节(在文件头位置多) 摘自:https://blog.csdn.net/u012173922/article/details/78849076 ...
- WAV格式文件无损合并&帧头数据体解析(python)(原创)
一,百度百科 WAV为微软公司(Microsoft)开发的一种声音文件格式,它符合RIFF(Resource Interchange File Format)文件规范,用于保存Windows平台的音频 ...
- wav格式文件、pcm数据
wav格式文件是常见的录音文件,是声音波形文件格式之一,wav 文件由文件头和数据体两部分组成. 文件头是我们在做录音保存到文件的时候,要存储的文件的说明信息,播放器要通过文件头的相关信息去读取数据播 ...
- Android音视频之AudioTrack播放音频(二)
前一篇讲了如何录制wav音频文件,本篇就来讲讲如何播放wav文件,这里就是使用AudioTrack来播放音频,确切的说是播放pcm格式数据,使用AudioTrack播放也没什么难度,主要就是将数据写入 ...
- Android音频开发之AudioTrack实时播放
前言: 其实在Android中录音可以用MediaRecord录音,操作比较简单.但是不能对音频进行处理.考虑到项目中做的是实时语音只能选择AudioRecord进行录音.然后实时播放也只能采用Aud ...
- Android音频开发之——如何播放一帧音频
本文重点关注如何在Android平台上播放一帧音频数据.阅读本文之前,建议先读一下<Android音频开发(1):基础知识>,因为音频开发过程中,经常要涉及到这些基础知识,掌握了这些重要的 ...
- 如何在苹果电脑上创建一个html格式文件,并在浏览器正确打开
之前一直使用windows系统的电脑,创建文件很简单,改格式也非常的简单.但换了苹果电脑,如何创建一个HTML文件?却把我给整蒙了. 首先,为什么mac上不能直接新建文本文件? 因为mac一都是以应用 ...
- linux下alsa架构音频驱动播放wav格式文件
#include<stdio.h> #include<stdlib.h> #include <string.h> #include <alsa/asoundl ...
- 播放一个wav文件
use mmsystem;SndPlaySound('hello.wav',SND_FILENAME or SND_SYNC) ///////////////////////////////////u ...
随机推荐
- ArrayBuffer and Base64
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8& ...
- js实现小球的弹性碰撞。
前 言 MYBG 小编最近在做自己的个人网站,其中就用到了一个小球碰撞检测的功能,想自己写,无奈本人能力不足啊(毕竟还是一个菜鸟)!!就想着找个插件用一下也好,可是找了好久也没有找到一个比较好用 ...
- Nginx-动态路由升级版
前几篇文章我们介绍了Nginx的配置.OpenResty安装配置.基于Redis的动态路由以及Nginx的监控. Nginx-OpenResty安装配置 Nginx配置详解 Nginx技术研究系列1- ...
- 前端基于react,后端基于.net core2.0的开发之路(2) 开发环境的配置,注意事项,后端数据初始化
前端环境配置 项目介绍文章:前端基于react,后端基于.net core2.0的开发之路(1) 介绍 1.VSCode安装 下载地址:https://code.visualstudio.com/Do ...
- python变量命名规则
在Python中使用变量时,需要遵守一些规则和指南.违反这些规则将引发错误,而指南旨在让你编写的代码更容易阅读和理解.请务必牢记下述有关变量的规则. 变量名只能包含字母.数字和下划线.变量名可以字 ...
- 天龙八步"细说浏览器输入URL后发生了什么
本文摘要: 1.DNS域名解析: 2.建立TCP连接: 3.发送HTTP请求: 4.服务器处理请求: 5.返回响应结果: 6.关闭TCP连接: 7.浏览器解析HTML: 8.浏览器布局渲染: 总结 输 ...
- PowerShell 操作 Azure Blob Storage
本文假设已经存在了一个 Azure Storage Account,需要进行文件的上传,下载,复制,删除等操作.为了方便查看 PowerShell 代码执行的结果,本文使用了 MS 发布的一个 Azu ...
- Tomcat:Can't load AMD 64-bit .dll on a IA 32 platform(问题记录)
从报错看,这主要是应为64位-32位不兼容导致的.好在,在报此错的情况下,tomcat还是跑起来了. 具体来说,从网上搜索的一些资料来看,应该是jdk版本与tomcat不一致 .tomcat我的是64 ...
- Dynamics 365创建电子邮箱字段包含值的联系人同时更改负责人的方法。
摘要: 本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复267或者20171129可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyon ...
- 条件注释判断IE版本
在学习Bootstra的时候看到这么一句话, <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media ...