前言

上一篇我们已经学习了PCM音频的保存格式,这一篇我们通过掌握的知识,完成PCM音频的单声道和双声道的互相转换。

正文

首先我们把上一篇的最核心部分贴出来:

我们首先完成单声道转双声道的操作。

单声道转双声道

单声道转双声道的基本原理:

由图可知,我们需要把单声道的每一份数据都拷贝一份到右声道,这样使用双声道播放就没有问题了。

首先我录制了一个音频保存到ArrayList中:

 private val recordThread = Thread(Runnable {val iMinBufferSize = AudioRecord.getMinBufferSize(Constants.SAMPLE_RATE,currentChannel,AudioFormat.ENCODING_PCM_16BIT)val audioRecord = AudioRecord(MediaRecorder.AudioSource.MIC,Constants.SAMPLE_RATE,currentChannel,AudioFormat.ENCODING_PCM_16BIT,iMinBufferSize)audioRecord.startRecording()monoByteList.clear()val recordBytes = ByteArray(iMinBufferSize)var lastTime = 0Lvar pcmSize = 0while (lastTime < recordTime * 1000000L) {val readSize = audioRecord.read(recordBytes, 0, recordBytes.size)// 保存音频数据到ArrayList中monoByteList.addAll(recordBytes.asList())pcmSize += readSizelastTime = pcmSize * 1000000L / 2 / Constants.SAMPLE_RATE}audioRecord.stop()audioRecord.release()recordCallback()}
)

录制的是16位的数据,所以我们每一个采样的数据会占据两位,所以在拷贝的过程中,我们也要每两位拷贝一次:

private val convertMonoToStereoThread = Thread(Runnable {// 单声道转双声道// 双声道的存储格式为 LRLRLR// 所以把左声道的内容拷贝到右声道即可for (index in 0 until monoByteList.size step 2) {// 目前保存的是16位的数据,所以要复制前两位stereoByteList.add(monoByteList[index])stereoByteList.add(monoByteList[index + 1])// 目前保存的是16位的数据,所以要复制前两位stereoByteList.add(monoByteList[index])stereoByteList.add(monoByteList[index + 1])}convertCallback()
})

单声道转声道的操作就完成了。

双声道转单声道的操作

双声道转单声道的原理:

双声道转单声道有两种做法:
1、丢弃其中一路数据(丢失左声道或右声道的数据)
2、两路数据相加的平局值。(也可以是其他算法)

第一种做法:丢弃一路数据

我们可以按照单声道双声道的做法,每四位取前两位或后两位的数据即可。但是这里我们换一种做法。

// 保存了录制的16位双声道音频数据,过程省略,里面保存类型Byte
stereoByteList// 目标输出ArrayList,类型为Short,如果你需要Byte数据,可以再自行转换一次
monoByteList// 开始转换
private fun convertStereoToMono() {thread {// 双声道转单声道// 方案1:丢掉一路数据,此方法最简单// 这里只取左声道的声音monoByteList.clear()// ByteOrder.LITTLE_ENDIAN 从小到大 ,高位在后// ByteOrder.BIG_ENDIAN 从大到小,高位在前,默认val shortBuffer = ByteBuffer.wrap(stereoByteList.toByteArray()).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer()for (index in 0 until shortBuffer.capacity() step 2) {monoByteList.add(shortBuffer.get(index))}convertCallback()}}

这里我们使用了ByteBuffer帮助我们把Byte转成Short。其中有一个很重要的坑,就是设置Byte转Short的规则:

ByteOrder.LITTLE_ENDIAN 从小到大 ,高位在后
ByteOrder.BIG_ENDIAN 从大到小,高位在前,默认

short的长度为16位,所以需要两个8位的Byte一起保存,其中一个Byte保存的是前8位,也就是高位另外的一个Byte保存的后8位,也就是低位。


所以我们一定要确保高低位的顺序,否则得到的Short一定是错的,经过测试,录制的音频是低位在前,所以我们修改ByteBuffer默认的高位在前的配置:

ByteBuffer.wrap(stereoByteList.toByteArray()).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer()
// 读取指定位置的Short
val short = shortBuffer.get(index)

相同的原理,我们需要Byte转Int都可以借助对应的Buffer进行读取,非常的方便。

第二种做法:左右声道取平局值

// 保存了录制的16位双声道音频数据,过程省略,里面保存类型Byte
stereoByteList// 目标输出ArrayList,类型为Short,如果你需要Byte数据,可以再自行转换一次
monoByteListprivate fun convertStereoToMono() {thread {// 双声道转单声道monoByteList.clear()// ByteOrder.LITTLE_ENDIAN 从小到大 ,高位在后// ByteOrder.BIG_ENDIAN 从大到小,高位在前,默认val shortBuffer = ByteBuffer.wrap(stereoByteList.toByteArray()).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer()// 方案二:把左右声道的声音相加,取平均值// 使用kotlin的位运算 and shl等,无法得到正确的byte转short,short转initfor (index in 0 until shortBuffer.capacity() step 2) {monoByteList.add((shortBuffer.get(index) + shortBuffer.get(index + 1) / 2).toShort())}convertCallback()}}

基本流程和第一种方法一样,如果是你用的Java,你还可以通过位运算进行Short和Byte的转换,但是kotlin的对应的运算符却无法正确转换,具体原因还不清楚,这也是为什么我使用了Buffer进行转换的原因。

总结

只要我们掌握了PCM的保存格式,单声道和双声道的互相转换还是非常轻松的,下一篇我们来了解一下新的音频格式:WAV。

Android音视频系列(七):PCM音频单声道与双声道的相互转换相关推荐

  1. android声音播放函数双声道合并,Android音视频系列(七):PCM音频单声道与双声道的相互转换...

    前言 上一篇我们已经学习了PCM音频的保存格式,这一篇我们通过掌握的知识,完成PCM音频的单声道和双声道的互相转换. 正文 首先我们把上一篇的最核心部分贴出来: PCM音频保存格式 我们首先完成单声道 ...

  2. 声道切换 android,[RK3288][Android6.0] Audio中的单声道到双声道的转换处理过程

    Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 播放音乐是单声道,硬件用的是双声道. AudioFlinger::PlaybackThread:: ...

  3. android音视频【十】音频mp3剪切

    人间观察 为了等你,我错过了等我的人. 介绍 Android中在一些短视频的制作app软件上,会有给视频增加背景音乐的功能,而背景音乐/歌曲(一般是mp3)是从服务器上下载后,然后本地解码,往往用户会 ...

  4. matlab双声道转单声道,单声道和双声道的区别是什么

    区别:1.单声道是指一个声音的通道:双声道是指有两个声音的通道.2.单声道是把来自不同方位的音频信号混合后统一由录音器材把它记录下来,再由一个扬声器进行重放:双声道是在空间放置两个互成一定角度的扬声器 ...

  5. [Android] [音视频系列]在 Android 平台使用 AudioRecord 和 AudioTrack API 完成音频 PCM 数据的采集和播放,并实现读写音频 wav 文件

    参考 官方文档地址:https://developer.android.google.cn/reference/android/media/AudioRecord GitHub 地址:https:// ...

  6. Android音视频系列(八):了解音频格式WAV以及与PCM的转换

    前言 之前我们已经了解了PCM音频数据,我们理解为最原始的数据,虽然他的音质是最棒的,但是同时也暴露出两个很重要的问题: 普通播放器无法播放,数据里不包含任何跟音频格式有关的信息(声道,采样率等等): ...

  7. Android音频格式转换,Android音视频系列(八):了解音频格式WAV以及与PCM的转换...

    前言 之前我们已经了解了PCM音频数据,我们理解为最原始的数据,虽然他的音质是最棒的,但是同时也暴露出两个很重要的问题: 普通播放器无法播放,数据里不包含任何跟音频格式有关的信息(声道,采样率等等): ...

  8. PCM音频单声道立体声转换C代码实现

    目录 一.PCM介绍 二.PCM参数 三.单声道.立体声转换 一.PCM介绍 PCM(Pulse Code Modulation),脉冲编码调制.人耳听到的是模拟信号,PCM是把声音从模拟信号转化为数 ...

  9. [RK3288][Android6.0] Audio中的单声道到双声道的转换处理过程

    Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 播放音乐是单声道,硬件用的是双声道. AudioFlinger::PlaybackThread:: ...

  10. 【Android音视频】OpenSL ES音频播放示例一

    本文将实现一个使用OpenSL ES来播放assets目录下mp3歌曲的demo(实际推荐大家使用oboe库). Android NDK之高性能音频https://developer.android. ...

最新文章

  1. 正则表达式语法规则收集
  2. com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'rtjhyt' in 'field list'
  3. 【392天】跃迁之路——程序员高效学习方法论探索系列(实验阶段149-2018.03.04)...
  4. spoj Pattern Find(kmp)
  5. 数组反向遍历ios_iOS开发实用技巧—Objective-C中的各种遍历(迭代)方式
  6. (学习笔记)Oracle表空间相关基本命令
  7. lenovo Think Centre TCM 开发环境搭建
  8. 计算机应用头部案例提交,基于头部位置的应用程序放置的制作方法
  9. 不同分辨率图片匹配_超实用的图像超分辨率重建方法及应用介绍
  10. 央视被黑内幕,居然存在暴库及上传漏洞
  11. 这两年亚马逊创业都是一个非常火热的话题
  12. Mac 2016 运行Emacs,M-x键失效问题
  13. POJ1961 Period
  14. 颜色模式中8位,16位,24位,32位色彩是什么意思?会有什么区别?计算机颜色格式( 8位 16位 24位 32位色)【转】...
  15. HBase Shell 命令私藏
  16. 华为电脑管家装到D盘_春节过后你的电脑该减肥了 Win10清理C盘瘦身全攻略
  17. AppStore下载数据查看
  18. NLP实践——基于SBERT的语义搜索,语义相似度计算,SimCSE、GenQ等无监督训练
  19. OpenCV函数subtract()使用心得及需要注意的地方
  20. 百度网盘简易下载工具介绍

热门文章

  1. 新手购买单反终极攻略--谈谈现场验机的要领与要点
  2. Android的Gallery3D模块介绍
  3. 中小型企业网络配置、基于企业网络方案的设计与实施
  4. 【转载】中文知识图谱研讨会的学习总结 (上) 图谱引入、百度知心、搜狗知立...
  5. 如何复制百度文库的内容
  6. [教程] MPICH2 Win7 VS2008环境搭建
  7. roller java,月光软件站 - 编程文档 - Java - 修改ReadMorePlugin.java,使其支持中文标题(roller webblog)...
  8. WebRTC之SDP篇
  9. Unity——JSON
  10. 基于51单片机超声波测距仪设计倒车雷达防撞报警器