㈠ android开发中如何实现播放声音时根据声音大小做一个动态能量条
音量变化是比较频繁的,通常是低于50毫秒。
所以你取得的音量大小是一条密集的点组成的曲线,要动态的展示这些密集的点,有两种不同的实现方式,效果也不一致。
把这些点分成不同的区间。比如5个区间,每个区间对应一张图片(也可以是动态的图片AnimationDrawable),取得音量后判断是哪个区间,设置对应的图片。打个比方,如果是用格数来表示,就有五张图片表示1-5格,音量最大值可以通过mediaRecorder.getMaxAmplitude()获得。分成五个区间,每个区间显示对应格数的图片。当然,这个图片也可以是类似水波在荡漾的动态图片,用AnimationDrawable来播放。
平滑的处理这些点。把上一次的位置与这一次的位置做一个动画,如果采集频率为50毫秒,动画的ration就是50ms。比如是做一个圆圈,随着音量大小变化,那么可以参照如下的属性动画:
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("scaleX", lastScale, scale);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleY", lastScale, scale);
valumeAnimator = ObjectAnimator.ofPropertyValuesHolder(speakVolumeImageV, pvhX, pvhY);
valumeAnimator.setDuration(MIN_TIME).start();
通过放大和缩小speakVolumeImageV来展示当前音量。其中scale是此次的缩放比,lastScale是上次的缩放比。缩放比初始为1.0f,如果最大只能放大到speakVolumeImageV的两倍大,那么可以这样计算
Float scale = 1.0f;
if (volume == 0) {
scale = 1.0f;
} else {
scale = 1.0f + (volume / MAX_VOLUME * 1.0f);
}
㈡ Android音频播放
最近需要在Android的客户端中使用PCM声音播放和录制,简单学习了一下。有不正确的地方还请指出。
首先有几个概念需要了解一下:采样频率、声道数、采样位数。
采样频率一般是sample rate, 代表的是数字化音频时每秒采样的次数。常见的有44.1KHz(CD品质)、48KHz等。
这个很好理解,单声道Mono就是声音从一个方向传出来;双声道Stereo也叫立体声,声音是从两个方向传来。通常的流行音乐中,仔细听能发现每个声道可能侧重不同的乐曲声部,比如左声道吉他,右声道钢琴,人声似乎两个声道都有,听起来就像站在中间一样。(这里没有考证,随便举例)
每一个采样都是一个数据点,采样位数是指这个数据点使用了几位来记录。AudioTrack类只支持8位和16位的PCM音频。8位就是2的8次方,即256个值;而16位则是2的16次方,有65536个值。
这个在音频的编解码中还是比较常用的。在PCM格式中,1秒钟音频的数据大小是SampleRate×Channel×Bit/8,单位是byte字节。由于PCM本身没有音频帧的概念,所以通过这个公式就能计算出任意时长音频的大小,或者得到任意大小音频的时长。如果规定1个音频帧是“每个声道256个采样”,双声道下就是512个采样,那么1帧的数据量就是256×Channel×Bit/8,同理可以推断出1秒钟有多少音频帧等等。音频帧的概念在各种编解码中各有不同,但计算公式大同小异,这里不展开。
Android中音频的播放使用的是AudioTrack类,具体用法非常简单。
首先设置buffer大小。AudioTrack播放时需要先写入buffer,如果这个buffer没有写满,那么这部分是不会播放的。所以buffer不能设置太小,这样会导致播放不连贯;而buffer也不能设置太小,这样不间断写入会消耗许多CPU资源。AudioTrack自带了getMinBufferSize方法可以给出一个最小buffer,一般用这个值就可以。getMinBufferSize方法三个参数分别是sample rate、channel和bit。
设置完buffer size就可以实例化一个AudioTrack。其中第一个参数streamType是指不同的音频流类型,包括STREAM_MUSIC、STREAM_ALARM、STREAM_VOICE_CALL、STREAM_RING等,是Android对不同音频的分类。中间三个参数很好理解,第四个是buffer size,刚刚计算出来了。最后一个参数mode有两种:MODE_STREAM和MODE_STATIC。前者是以流形式播放,后者则是一次性全部写入然后播放。
调用实例的play()方法就可以开始播放了。不过播放得要有数据吧?要填写数据就要用到write()方法。write方法中第一个参数是一个byte[]类型,是要写入的数据源,可以是从文件流中读取出来的;第二个参数offset是初始位移,即从source的哪个位置开始;第三个参数则是输入长度。
当write方法写满一个AudioTrack的buffer时,就会有声音播放出来了。
当播放完成后记得要把AudioTrack停止并释放。
㈢ Android音视频【八】音频基础
前些文章讲了视频,我们开始音频。
开始介绍前,先看一个声音的波形图:
声音是一种压力波,当敲击键盘或者撞击物体时,它们的震动会引起空气的震动,使空气产生疏密变化,由此就形成了一种声波。
声波的三要素是频率、振幅、和波形,频率代表音阶的高低,振幅代表响度,波形代表音色。
频率 : 频率 越高,波长越短,低频声响的波长则越长,所以更容易越过障碍物,能量衰减就小,声音传播的就远。反之则会得到相反的结论。
振幅:用不同的力度敲击物体,它的声音大小不一样,它的能量也不一样,声音越大振幅越高。
波形/音色: 音色就是在同样的频率(音调)和响度(振幅)下,敲击键盘或者撞击物体是完全不同的。波的形状代表了声音的音色。
如何进行声音进行保存呢? 对声音的采样常用麦克风等设备将声音信号转换成电信号,再用模/数转换器将电信号转换成一串用1和0表示的二进制数字(数字信号)。每秒对声音采样上万次,获得上万个按照时间顺序排列的二进制数字,然后将连续变化不断的声音转化成了计算机可储存并识别的二进制数字。
为了将模拟信号数字化,需要进行:采样,量化,编码。
首先要对模拟信号进行采样,所谓采样就是在时间轴上对信号进行数字化。根据奈奎斯特定理(也称采样定理),按比声音最高频率高 2 倍以上的频率对声音进行采样,对于高质量的音频信号,其频率范围在 20Hz ~ 20kHz ,所以采样频率一般为 44.1kHz ,这样就保证采样声音达到 20kHz 也能被数字化,从而使得经过数字化处理之后,人耳听到的声音质量不会被降低。而所谓的 44.1 kHz 就是代表 1 s 会采样 44100 次。
每个采样又该如何表示呢?进行量化。量化是指在幅度轴上对信号进行数字化。量化位数越大,声音的质量越高。常用的量化位数有8位、16位和32位。量化位数指用几位二进制数来存储采样获得的数据。量化位数为8即指用8位二进制数来存储数据,如0001011
比如用 16 bit 的二进制信号来表示声音的一个采样,而 16 bit 所表示的范围是 [-32768 , 32767] , 共有 65536 个可能取值,因此最终模拟的音频信号在幅度上也分为了 65536 层。
编码,就是按照一定的格式记录采样和量化后的数字数据,比如顺序存储或压缩存储等等。
这里涉及了很多种格式,通常所说的音频的裸数据就是 PCM (Pulse Code Molation) 数据。描述一段 PCM 数据一般需要以下几个概念:量化格式(sampleFormat)、采样率(sampleRate)、声道数 (channel) 。以 CD 的音质为例:量化格式为 16 bit (2 byte),采样率 44100 ,声道数为 2 ,这些信息就描述了 CD 的音质。而对于声音的格式,还有一个概念用来描述它的大小,称为数据比特率,即 1s 时间内的比特数目,它用于衡量音频数据单位时间内的容量大小。而对于 CD 音质的数据,比特率为多少呢? 计算如下:
那么在一分钟里,这类 CD 音质的数据需要占据多大的存储空间呢?计算如下:
当然,如果 sampleFormat 更加精确 (比如用 4 个字节来描述一个采样),或者 sampleRate 更加密集 (比如 48kHz 的采样率), 那么所占的存储空间就会更大,同时能够描述的声音细节就会越精确。存储的这段二进制数据即表示将模拟信号转为数字信号了,以后就可以对这段二进制数据进行存储,播放,复制,或者进行其它操作。
关于这3个过程,可以看下这篇文章,图形表示采样,量化,编码的过程更容易理解。 https://www.bilibili.com/read/cv1771961/
所以说:
1)采样:在时间轴上对信号数字化;
2)量化:在幅度轴上对信号数字化;
3)编码:按一定格式记录采样和量化后的数字数据。
声道(sound channel)是指声音在录制或播放时在不同空间位置采集或回放的相互独立的音频信号,所以声道数也就是声音录制时的声音源数量或者回放时相应的扬声器数量。
常见的有:单声道,立体声道,4声道,5.1声道,7.1声道等。在移动端一般是单声道,立体声道。
上面提到了 CD 音质的数据采样格式,曾计算出每分钟需要的存储空间约为 10.09 MB ,如果仅仅是将其存储在光盘或者硬盘中,可能是可以接受的,但是若要在网络中实时在线传输的话,那么这个数据量可能就太大了,所以必须对其进行压缩编码。压缩编码的基本指标之一就是压缩比,压缩比通常小于 1 。压缩算法包括有损压缩和无损压缩。无损压缩是指解压后的数据可以完全复原。在常用的压缩格式中,用的较多的是有损压缩,有损压缩是指解压后的数据不能完全恢复,会丢失一部分信息,压缩比越小,丢失的信息就比越多,信号还原后的失真就会越大。根据不同的应用场景 (包括存储设备、传输网络环境、播放设备等),可以选用不同的压缩编码算法,如 PCM 、WAV、AAC 、MP3 、Ogg 等。
WAV 编码就是在 PCM 数据格式的前面加了 44 个字节,分别用来存储 PCM 的采样率、声道数、数据格式等信息。
特点: 音质好,大量软件支持。
场景: 多媒体开发的中间文件、保存音乐和音效素材。
MP3 具有不错的压缩比,使用 LAME 编码 (MP3 编码格式的一种实现)的中高码率的 MP3 文件,听感上非常接近源 WAV 文件,当然在不同的应用场景下,应该调整合适的参数以达到最好的效果。
特点: 音质在 128 Kbit/s 以上表现还不错,压缩比比较高,大量软件和硬件都支持,兼容性好。
场景: 高比特率下对兼容性有要求的音乐欣赏。
AAC 是新一代的音频有损压缩技术,它通过一些附加的编码技术(比如 PS 、SBR) 等,衍生出了 LC-AAC 、HE-AAC 、HE-AAC v2 三种主要的编码格式。LC-AAC 是比较传统的 AAC ,相对而言,其主要应用于中高码率场景的编码 (>=80Kbit/s) ; HE-AAC 相当于 AAC + SBR 主要应用于中低码率的编码 (<= 80Kbit/s); 而新推出的 HE-AAC v2 相当于 AAC + SBR + PS 主要用于低码率场景的编码 (<= 48Kbit/s) 。事实上大部分编码器都设置为 <= 48Kbit/s 自动启用 PS 技术,而 > 48Kbit/s 则不加 PS ,相当于普通的 HE-AAC。
特点: 在小于 128Kbit/s 的码率下表现优异,并且多用于视频中的音频编码。
场景: 128 Kbit/s 以下的音频编码,多用于视频中音频轨的编码。
Ogg 是一种非常有潜力的编码,在各种码率下都有比较优秀的表现,尤其是在中低码率场景下。Ogg 除了音质好之外,还是完全免费的,这为 Ogg 获得更多的支持打好了基础,Ogg 有着非常出色的算法,可以用更小的码率达到更好的音质,128 Kbit/s 的 Ogg 比 192kbit/s 甚至更高码率的 MP3 还要出色。但是目前因为还没有媒体服务软件的支持,因此基于 Ogg 的数字广播还无法实现。Ogg 目前受支持的情况还不够好,无论是软件上的还是硬件上的支持,都无法和 MP3 相提并论。
特点: 可以用比 MP3 更小的码率实现比 MP3 更好的音质,高中低码率下均有良好的表现,兼容性不够好,流媒体特性不支持。
场景: 语言聊天的音频消息场景。
压缩编码的原理实际上就是压缩调冗余信号,冗余信号是指哪些不能被人感知到的信号,包含人耳听觉范围之外的音频信号以及被屏蔽掉的音频信号等,这些冗余信息进行编码处理。
一般在音视频通话,直播中,短视频,以及大部分的视频都是用aac编码。
本篇主要是介绍了音频的一些基础知识和概念,对于后续学习音频的编解码有帮助,而不会一脸懵。
备注
㈣ android音视频开发一安卓常用API
Android SDK 提供了两套音频采集的API,分别是:MediaRecorder 和 AudioRecord,前者是一个更加上层一点的API,它可以直接把手机麦克风录入的音频数据进行编码压缩(如AMR、MP3等)并存成文件,而后者则更接近底层,能够更加自由灵活地控制,可以得到原始的一帧帧PCM音频数据。如果想简单地做一个录音机,录制成音频文件,则推荐使用 MediaRecorder,而如果需要对音频做进一步的算法处理、或者采用第三方的编码库进行压缩、以及网络传输等应用,则建议使用 AudioRecord,其实 MediaRecorder 底层也是调用了 AudioRecord 与 Android Framework 层的 AudioFlinger 进行交互的。直播中实时采集音频自然是要用AudioRecord了。
2.1 播放声音可以用MediaPlayer和AudioTrack,两者都提供了java API供应用开发者使用。虽然都可以播放声音,但两者还是有很大的区别的。
2.2 其中最大的区别是MediaPlayer可以播放多种格式的声音文件,例如MP3,AAC,WAV,OGG,MIDI等。MediaPlayer会在framework层创建对应的音频解码器。而AudioTrack只能播放已经解码的PCM流,如果对比支持的文件格式的话则是AudioTrack只支持wav格式的音频文件,因为wav格式的音频文件大部分都是PCM流。AudioTrack不创建解码器,所以只能播放不需要解码的wav文件。
2.3 MediaPlayer在framework层还是会创建AudioTrack,把解码后的PCM数流传递给AudioTrack,AudioTrack再传递给AudioFlinger进行混音,然后才传递给硬件播放,所以是MediaPlayer包含了AudioTrack。
2.4 在接触Android音频播放API的时候,发现SoundPool也可以用于播放音频。下面是三者的使用场景:MediaPlayer 更加适合在后台长时间播放本地音乐文件或者在线的流式资源; SoundPool 则适合播放比较短的音频片段,比如游戏声音、按键声、铃声片段等等,它可以同时播放多个音频; 而 AudioTrack 则更接近底层,提供了非常强大的控制能力,支持低延迟播放,适合流媒体和VoIP语音电话等场景。
使用 Camera API 采集视频数据并保存到文件,分别使用 SurfaceView、TextureView 来预览 Camera 数据,取到 NV21 的数据回调。
4.1 一个音视频文件是由音频和视频组成的,我们可以通过MediaExtractor、MediaMuxer把音频或视频给单独抽取出来,抽取出来的音频和视频能单独播放;
4.2 MediaMuxer的作用是生成音频或视频文件;还可以把音频与视频混合成一个音视频文件。
文献资料 https://www.cnblogs.com/renhui/p/7452572.html
㈤ Android音视频【十二】使用OpenSLES和AudioTrack进行播放PCM
本节我们学习下如何播放pcm数据,在Android中有两种方法:一种是使用java层的 AudioTrack 方法,一种是使用底层的 OpenSLES 直接在 jni 层调用系统的 OpenSLES的c方法 实现。
两种使用场景不一样:
AudioTrack 一般用于 比如本地播放一个pcm文件/流,又或者播放解码后的音频的pcm流,API较简单。
OpenSLES 一般用于一些播放器中开发中,比如音频/视频播放器,声音/音频的播放采用的OpenSLES,一是播放器一般是c/c++实现,便于直接在c层调用OpenSLES的API,二也是如果用AudioTrack进行播放,务必会带来java和jni层的反射调用的开销,API较复杂。
可以根据业务自行决定来进行选择。
AudioTrack的方式使用较简单,直接在java层。
指定采样率,采样位数,声道数进行创建。
其中44100是采样率, AudioFormat.CHANNEL_OUT_STEREO 为双声道,还有 CHANNEL_OUT_MONO 单声道。 AudioFormat.ENCODING_PCM_16BIT 为采样位数16位,还有 ENCODING_PCM_8BIT 8位。 minBufferSize 是播放器缓冲的大小,也是根据采样率和采样位数,声道数 进行获取,只有满足最小的buffer才去操作底层进程播放。
最后一个参数mode。可以指定的值有 AudioTrack.MODE_STREAM 和 AudioTrack.MODE_STATIC 。
MODE_STREAM 适用于大多数的场景,比如动态的处理audio buffer,或者播放很长的音频文件,它是将audio buffers从java层传递到native层。音频播放时音频数据从Java流式传输到native层的创建模式。
MODE_STATIC 适用场景,比如播放很短的音频,它是一次性将全部的音频资源从java传递到native层。音频数据在音频开始播放前仅从Java传输到native层的创建模式。
是的,就这么一个方法。注意此方法是同步方法,是个耗时方法,一般是开启一个线程循环调用 write 方法进行写入。
注意在调用 write 方法前需要调用 audioTrack.play() 方法开始播放。
因为是pcm裸数据,无法像mediaplayer一样提供了API。所以需要自己处理下。可以利用 getPlaybackHeadPosition 方法。
getPlaybackHeadPosition() 的意思是返回以帧为单位表示的播放头位置
getPlaybackRate() 的意思是返回以Hz为单位返回当前播放采样率。
所以当前播放时间可以通过如下方式获取
OpenSLES:(Open Sound Library for Embedded Systems).
OpenSLES是跨平台是针对嵌入式系统精心优化的硬件音频加速API。使用OpenSLES进行音频播放的好处是可以不依赖第三方。比如一些音频或者视频播放器中都是用OpenSLES进行播放解码后的pcm的,这样免去了和java层的交互。
在Android中使用OpenSLES首先需要把Android 系统提供的so链接到外面自己的so。在CMakeLists.txt脚本中添加链接库OpenSLES。库的名字可以在 类似如下目录中
需要去掉lib
然后导入头文件即可使用了OpenSLES提供的底层方法了。
创建&使用的步骤大致分为:
一个 SLObjectItf 里面可能包含了多个Interface,获取Interface通过 GetInterface 方法,而 GetInterface 方法的地2个参数 SLInterfaceID 参数来指定到的需要获取Object里面的那个Interface。比如通过指定 SL_IID_ENGINE 的类型来获取 SLEngineItf 。我们可以通过 SLEngineItf 去创建各种Object,例如播放器、录音器、混音器的Object,然后在用这些Object去获取各种Interface去实现各种功能。
如上所说,SLEngineItf可以创建混音器的Object。
在创建播放器前需要创建音频的配置信息(比如采样率,声道数,每个采样的位数等)
开始播放后会不断的回调这个 pcmBufferCallBack 函数将音频数据压入队列
(*pcmBufferQueue)->RegisterCallback(pcmBufferQueue, pcmBufferCallBack, this);
如果想要暂停播放参数直接设置为SL_PLAYSTATE_PAUSED,若暂停后继续播放设置参数为SL_PLAYSTATE_PLAYING即可。若想要停止播放参数设置为SL_PLAYSTATE_STOPPED即可。
首先获取播放器的用于控制音量的接口SLVolumeItf pcmVolumePlay
然后动态设置
首先也是获取播放器的用于控制音量的接口SLMuteSoloItf pcmMutePlay
然后动态设置
看起来控制还是蛮简单的哈。先熟悉这么多,OpenSLES还是蛮强大的。
https://github.com/ta893115871/PCMPlay