日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > Android >内容正文

Android

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

發(fā)布時(shí)間:2023/12/2 Android 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android音视频系列(七):PCM音频单声道与双声道的相互转换 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

上一篇我們已經(jīng)學(xué)習(xí)了PCM音頻的保存格式,這一篇我們通過(guò)掌握的知識(shí),完成PCM音頻的單聲道和雙聲道的互相轉(zhuǎn)換。

正文

首先我們把上一篇的最核心部分貼出來(lái):

我們首先完成單聲道轉(zhuǎn)雙聲道的操作。

單聲道轉(zhuǎn)雙聲道

單聲道轉(zhuǎn)雙聲道的基本原理:

由圖可知,我們需要把單聲道的每一份數(shù)據(jù)都拷貝一份到右聲道,這樣使用雙聲道播放就沒有問題了。

首先我錄制了一個(gè)音頻保存到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)// 保存音頻數(shù)據(jù)到ArrayList中monoByteList.addAll(recordBytes.asList())pcmSize += readSizelastTime = pcmSize * 1000000L / 2 / Constants.SAMPLE_RATE}audioRecord.stop()audioRecord.release()recordCallback()} )

錄制的是16位的數(shù)據(jù),所以我們每一個(gè)采樣的數(shù)據(jù)會(huì)占據(jù)兩位,所以在拷貝的過(guò)程中,我們也要每?jī)晌豢截愐淮?#xff1a;

private val convertMonoToStereoThread = Thread(Runnable {// 單聲道轉(zhuǎn)雙聲道// 雙聲道的存儲(chǔ)格式為 LRLRLR// 所以把左聲道的內(nèi)容拷貝到右聲道即可for (index in 0 until monoByteList.size step 2) {// 目前保存的是16位的數(shù)據(jù),所以要復(fù)制前兩位stereoByteList.add(monoByteList[index])stereoByteList.add(monoByteList[index + 1])// 目前保存的是16位的數(shù)據(jù),所以要復(fù)制前兩位stereoByteList.add(monoByteList[index])stereoByteList.add(monoByteList[index + 1])}convertCallback() })

單聲道轉(zhuǎn)聲道的操作就完成了。

雙聲道轉(zhuǎn)單聲道的操作

雙聲道轉(zhuǎn)單聲道的原理:

雙聲道轉(zhuǎn)單聲道有兩種做法:
1、丟棄其中一路數(shù)據(jù)(丟失左聲道或右聲道的數(shù)據(jù))
2、兩路數(shù)據(jù)相加的平局值。(也可以是其他算法)

第一種做法:丟棄一路數(shù)據(jù)

我們可以按照單聲道雙聲道的做法,每四位取前兩位或后兩位的數(shù)據(jù)即可。但是這里我們換一種做法。

// 保存了錄制的16位雙聲道音頻數(shù)據(jù),過(guò)程省略,里面保存類型Byte stereoByteList// 目標(biāo)輸出ArrayList,類型為Short,如果你需要Byte數(shù)據(jù),可以再自行轉(zhuǎn)換一次 monoByteList// 開始轉(zhuǎn)換 private fun convertStereoToMono() {thread {// 雙聲道轉(zhuǎn)單聲道// 方案1:丟掉一路數(shù)據(jù),此方法最簡(jiǎn)單// 這里只取左聲道的聲音monoByteList.clear()// ByteOrder.LITTLE_ENDIAN 從小到大 ,高位在后// ByteOrder.BIG_ENDIAN 從大到小,高位在前,默認(rèn)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轉(zhuǎn)成Short。其中有一個(gè)很重要的坑,就是設(shè)置Byte轉(zhuǎn)Short的規(guī)則:

ByteOrder.LITTLE_ENDIAN 從小到大 ,高位在后
ByteOrder.BIG_ENDIAN 從大到小,高位在前,默認(rèn)

short的長(zhǎng)度為16位,所以需要兩個(gè)8位的Byte一起保存,其中一個(gè)Byte保存的是前8位,也就是高位另外的一個(gè)Byte保存的后8位,也就是低位。


所以我們一定要確保高低位的順序,否則得到的Short一定是錯(cuò)的,經(jīng)過(guò)測(cè)試,錄制的音頻是低位在前,所以我們修改ByteBuffer默認(rèn)的高位在前的配置:

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

相同的原理,我們需要Byte轉(zhuǎn)Int都可以借助對(duì)應(yīng)的Buffer進(jìn)行讀取,非常的方便。

第二種做法:左右聲道取平局值

// 保存了錄制的16位雙聲道音頻數(shù)據(jù),過(guò)程省略,里面保存類型Byte stereoByteList// 目標(biāo)輸出ArrayList,類型為Short,如果你需要Byte數(shù)據(jù),可以再自行轉(zhuǎn)換一次 monoByteListprivate fun convertStereoToMono() {thread {// 雙聲道轉(zhuǎn)單聲道monoByteList.clear()// ByteOrder.LITTLE_ENDIAN 從小到大 ,高位在后// ByteOrder.BIG_ENDIAN 從大到小,高位在前,默認(rèn)val shortBuffer = ByteBuffer.wrap(stereoByteList.toByteArray()).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer()// 方案二:把左右聲道的聲音相加,取平均值// 使用kotlin的位運(yùn)算 and shl等,無(wú)法得到正確的byte轉(zhuǎn)short,short轉(zhuǎn)initfor (index in 0 until shortBuffer.capacity() step 2) {monoByteList.add((shortBuffer.get(index) + shortBuffer.get(index + 1) / 2).toShort())}convertCallback()}}

基本流程和第一種方法一樣,如果是你用的Java,你還可以通過(guò)位運(yùn)算進(jìn)行Short和Byte的轉(zhuǎn)換,但是kotlin的對(duì)應(yīng)的運(yùn)算符卻無(wú)法正確轉(zhuǎn)換,具體原因還不清楚,這也是為什么我使用了Buffer進(jìn)行轉(zhuǎn)換的原因。

總結(jié)

只要我們掌握了PCM的保存格式,單聲道和雙聲道的互相轉(zhuǎn)換還是非常輕松的,下一篇我們來(lái)了解一下新的音頻格式:WAV。

總結(jié)

以上是生活随笔為你收集整理的Android音视频系列(七):PCM音频单声道与双声道的相互转换的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。