Android音频: 如何使用AudioTrack播放一个WAV格式文件?

Android AudioTrack 爆音问题记录 项目场景: 通过AudioTrack播放wav音效文件 问题描述: 声音开头有爆音,“嗒”一声非常明显: 原因分析: 经过修改Buffer大小,采样率都不起作用,最后沉下心来查找资料,最终从wav文件格式入手,找到原因: wav文件格式包含文件头和文件体。 代码中从文件读取到buffer播放,没有跳过文件头。 有人说了,无图不真相,抄了一个图做纪念,方便查看。 解决方案: AudioTrack的write方法跳过头部,仅播放data域。 //先确定声道,采样率,播放类型,位数,根据实际情况进 阅读详情
翻译 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不是专门为数字音频设计,那么在继续我们的讨论之前,你可能需要先了解一些基本的缩略语。别担心,都很简单,我们不需要对此做深入挖掘。

  • 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音频数据的格式不一致。

在第一种情况,事情就很简单了,我们仅仅需要使用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开发:使用AudioRecord录音、将录音保存为wav文件、使用AudioTrack保存录音 一、使用AudioRrecord录音 1.1声明 首先需要声明一个AudioRecord类的实例。之所以需要事先声明,是因为在本例中,录音的启动和结束被封装在两个不同的方法里。而通常来讲,“开始录音”和“结束录音”在大部分时候也确实是需要拆分成两个不同的动作的。 private AudioRecord audioRecord; 除了声明AudioRecord的实例之外,我们还需要准备一些参数: // 采样率,现在能够保证在所有设备上使用的采样率是44100Hz, 但是其他的采样率(22050, 阅读详情

相关推荐

Android 音频开发——AudioTrack播放

 AudioTrack类用来管理、播放单个音频资源,它只能播放PCM数据,其他音频格式(aac、amr、opus、flac等)需要经过解码成PCM才能使用。 运行模式 AudioTrack可以运行两种模式: 流模式 静态模式 流模式  可以将连续的数据流写入AudioTrack,进行播放。播放音频数据块时,这种模式最有用。 如: 声音持续的时间太长,无法容纳在内存中; 由于音频数据的特性(太大的采样率,每个采样的比特数…)而太大而无法容纳在内存中; 在播放先前排队的音频时收到或生成的声音。 静态模式

奋斗的菜鸟ing 6461

Android使用AudioTrack播放WAV音频文件

其实WAV和PCM两者之间只差了一个wav文件头而已,所以实现了一套audiotrack播放wav的功能。同时支持本地文件播放和网络文件播放

GGGL的专栏 7320

android使用AudioTrack播放多个音频文件

因最近项目需要多个音频文件前后拼接播放,所幸较大致看了下android常用播放音频的几种方式。 MediaPlayer类--(常用于播放长音频)缺陷-音频播放前后延迟太高(实验前后拼接播放时间为10s,正常播放整段语音时间为6s)   SoundPool类--(常用播放游戏,点击声音等短音频)缺陷-需要提前加载数据,没有播放完毕监听   AudioTrack类-- MODE_ST

cqboy的专栏 1万+

android 使用MediaPlayer播放wav文件

在Android中使用MediaPlayer播放WAV文件 简介 在Android开发中,音频播放是一个常见需求。在各种音频格式中,WAV(Waveform Audio File Format)因其高音质和无损压缩而受到广泛欢迎。本篇文章将介绍如何在Android应用中使用MediaPlayer类来播放WAV文件,同时提供...

weixin_41264467的博客 1633

AudioTrack播放流程

AudioTrack介绍 音频播放声音分为MediaPlayer和AudioTrack两种方案的。MediaPlayer可以播放多种格式的声音文件,例如MP3,WAV,OGG,AAC,MIDI等。然而AudioTrack只能播放PCM数据流。当然两者之间还是有紧密的联系,MediaPlayer在播放音频时,在framework层还是会创建AudioTrack,把解码后的PCM数流传递给AudioTrack,最后由AudioFlinger进行混音,传递音频给硬件播放出来。利用AudioTrack播放只是跳过

画饼人的博客 3560

Android多媒体开发(5)————利用Android AudioTrack播放mp3文件

/********************************************************************************************  * author:conowen@大钟

[conowen]大钟的专栏 4万+

android 播放wav代码,播放简短的.wav文件 - Android

Raghav Sood..27该的Soundpool是这个正确的类.以下代码是如何使用它的示例.它也是我在我的几个应用程序中用来管理声音的代码.您可以根据自己的喜好(或内存允许)发出声音.public class SoundPoolPlayer {private SoundPool mShortPlayer= null;private HashMap mSounds = new HashMap()...

weixin_42175971的博客 2764

使用AudioTrack实现声音播放

这篇文章主要讲的是AudioTrack的用法。 AudioTrack 一般用来实现播放原始音频数据(PCM) 觉得这个类没有什么好说的,咱就直接上代码吧。 步骤一: 配置需要的参数,初始化AudioTrack对象。 我们需要了解一下这些参数 streamType 这个参数表示使用哪种音频管理策略 STREAM_VOCIE_CALL

爱做梦的梦想家 4348

[译]Android音频: 如何使用AudioTrack播放一个WAV格式文件?

原文链接:Android Audio: Play a WAV file on an AudioTrack 译者注: 1. 由于这是技术文章,所以有些词句使用原文,表达更准确。 2. 由于水平有效,有些地方可能翻译的不够准确,如有不当之处,敬请批评指正. 3. 针对某些语句,适当补充了上下文及更适合中文阅读,尽量做到信达雅。 如果你已经成功地了解了关于AudioTrack的一些话题,那

langwang2的专栏 4315

用AudioTrack播放wav

读取wav文件,用static模式播放.注意需要添加权限,

iTunes备份文件路径修改方法

因为以前的很多帖子iTunes都不是从微软下载的,而现在的iTunes一般都是微软商店里面的,它的路径为:“C:\Users\ 你的用户名 \Apple\MobileSync” (注:用户名,根据个人电脑名称更改)打开iTunes备份路径:“C:\Users\txh\AppData\Roaming\Apple Computer\MobileSync”,其中文件夹MobileSync就是用来存放备份文件的。创建你想备份纯在的目录创建备份文件目录(我的是G:/APPLE/Backup)2.粘贴的剪切文件目录。

尘玥的故事 1万+

Android音频: 怎样使用AudioTrack播放一个WAV格式文件?

翻译 By Long Luo 原文链接:Android Audio: Play a WAV file on an AudioTrack 译者注: 1. 因为这是技术文章,所以有些词句使用原文,表达更准确。 2. 因为水平有效,有些地方可能翻译的不够准确,如有不当之处,敬请批评指正. 3. 针对某些语句。适当补充了上下文及更适合中文阅读,尽量做到信达雅。 假设你已经成...

weixin_30296405的博客 854

5G R18标准:AI/ML如何重塑空口优化与网络架构

人工智能与机器学习正逐步成为通信网络优化的核心技术。其核心原理在于通过数据驱动模型,从海量网络数据中学习信道变化、用户行为等统计规律,替代传统的基于固定规则的优化方法。这一技术范式能显著提升网络性能与效率,尤其在空口资源管理、干扰协调和能耗控制等场景中价值突出。在5G Advanced(R18)标准化框架下,AI/ML被系统性地引入空口设计,聚焦于信道状态信息反馈增强、波束管理与预测等关键用例。通过定义网络侧AI、终端侧AI及联合学习的标准化接口与流程,R18旨在实现跨厂商设备的智能协同,推动无线网络向自适

weixin_35671843的博客 242

MediaPlayer和AudioTrack播放Audio的区别与联系

播放声音可以用MediaPlayer和AudioTrack,两者都提供了java API供应用开发者使用。虽然都可以播放声音,但两者还是有很大的区别的。 其中最大的区别是MediaPlayer可以播放多种格式的声音文件,例如MP3,AAC,WAV,OGG,MIDI等。MediaPlayer会在framework层创建对应的音频解码器。 而AudioTrack只能播放已经解码的PCM流,如果是文

u011361576的专栏 699

【OpenArm|Control】openarm机械臂ROS2仿真控制

openarm_ros2 存储库包含用于 ros2_control 的包集合。 它抽象化了硬件控制,将手臂公开为接收位置、速度和扭矩命令并输出关节状态的接口。 openarm_bringup 包提供了通过包与 ROS2 控制框架集成的启动文件。该软件包提供启动文件和配置,用于启动硬件接口、加载控制器以及将物理臂连接到 ROS2 生态系统。启动后,您可以使用标准 ROS2 控制工具和接口来命令手臂并接收反馈。openarm_bringup bringup 包支持模拟硬件(用于模拟/测试)和通过硬件插件的真实

ai领域探索者,欢迎一起交流,目前从事具身智能行业。 2953

LVGL9.2移植至正点原子阿波罗STM32F429开发板

LVGL(Light and Versatile Graphics Library)是一个开源的图形库,旨在为嵌入式系统提供高效、灵活的图形用户界面(GUI)解决方案。它具有小巧的内存占用和高性能的渲染能力,支持多种硬件平台,包括单片机、DSP 和 ARM 处理器等。LVGL 支持丰富的控件(如按钮、标签、图标、滚动条等),并提供强大的主题、样式和动画功能,使得开发者能够轻松设计出美观且响应迅速的界面。

m0_51149752的博客 3173

Android音视频开发基础(二) : 在Android平台使用AudioRecord和AudioTrack完成音频PCM数据的采集和播放,并实现读写音频wav文件

前言 在Android音视频开发中,网上知识点过于零碎,自学起来难度非常大,不过音视频大牛Jhuster提出了《Android 音视频从入门到提高 - 任务列表》。本文是Android音视频任务列表的第二篇, 对应的要学习的内容是:在Android平台使用AudioRecord和AudioTrack完成音频PCM数据的采集和播放,并实现读写音频wav文件 一、目录 (一)AudioRecord完成音频PCM数据的采集 1. 准备工作 1.1 AudioRecord API详解 AudioRecord

分享计算机基础,android,C/C++,音视频等内容 4970

【影像组学】用3Dslicer或Python提取影像组学特征

用3Dslicer或Python提取影像组学特征

abdslfwoeugp 1万+

Android 音频录音与播放

介绍音频的采集、编码、生成文件、转码等操作,通过 AudioRecord 采集音频,生成三种格式的文件格式(pcm、wav、aac),用 AudioStack 来播放这个音频。 一、PCM 、WAV、AAC 的文件头介绍 这里简单的介绍一下常见的三种音频格式: PCM :PCM(Pulse Code Modulation—-脉码调制录音)。所谓 PCM 录音就是将声音等模拟信号变成符号化的脉...

Android_SE的博客 9114

手把手教你用u-center配置ublox GPS模块:从波特率到报文频率全流程

本文提供了一份详细的u-center软件配置ublox GPS模块的完整指南。从硬件连接到软件设置,逐步讲解了如何探测并设置正确的波特率、选择与启用关键的NMEA和UBX报文,以及配置输出频率。文章特别强调了配置固化的重要性,并分享了使用UBX协议进行高效数据解析等高级技巧,帮助开发者快速上手并优化GPS模块性能。

weixin_28285943的博客 895

NXP MCU CAN波特率(位时间)配置详解

1. 概述 本文将会详细讲解如何设置NXP MCU的CAN波特率、位时间、采样点等属性。 波特率即CAN总线传输频率,位时间是波特率的倒数,例如波特率是500K,那么位时间 = 1 / 500000 = 0.000002s = 2000ns。 我们先来看一下S32K144单片机的EB中与CAN总线波特率相关的可配置参数有哪些: ...

weixin_42967006的博客 9690

Android 音视频开发(一) -- 使用AudioRecord 录制PCM(录音);AudioTrack播放音频

前言,音视频这块,确实比较难入门,本着学习的态度,我这边也跟着 Android 音视频开发入门指南 打怪升级,留下个脚印,大家共勉。 今天要完成的功能如下; 使用AudioRecord 进行录音 生成 wav 格式的音频,并进行播放 使用 AudioTrack 播放 pcm 格式音频 (Stream 和 static 模式) 由于声音不好上动图,只能来一张静图了,具体代码看工程:音视频学习Demo 一、基础知识 首先,我们先要了解声音是怎么被保存的起来的。在我们的世界中,声音是连续不断的,是一种模.

夏至的稻穗的博客 1万+

复现论文报错解决

博主复现论文代码过程中遇到的各种报错及解决方案~

rellvera的博客 1057

使用AudioTrack播放wav文件

近期使用AudioTrack播放wav文件的时候出现开头爆音的现象,分析后得知wav文件有个44或46字节的头数据,在使用AudioTrack播放写入数据时要把这个头数据去掉即可。源码实例如下: private static final int STREAM_TYPE = AudioManager.STREAM_MUSIC;//音频流类型 private static final int SAMPLERATE_HZ = 16000;//采样率 private static final...

Arise_Yang的博客 1397

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

上一篇: 如何通过笔迹分析一个人的性格?
下一篇: 第一个Web前端开发项目:天气应用
tcpipstack
博客等级 码龄14年 329粉丝 89原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值