Android 智能语音相关知识-PCM设备

这里记录一下自己对底层语音相关知识的学习和理解

一、Audio说明

Android上的应用一般都是通过AudioTrack类来播放音频,通过AudioRecord类来录制音频。AudioTrack类和AudioRecord类是Android Frameworks封装提供给应用使用的音频接口类。这些类经过层层的Binder、JNI等调用后会调用Audio HAL层提供的相关接口。这些接口实现了对音频设备、通路等一系列操作。就这样最终完成Android App和硬件的交互,实现声音的播放或者录制。
我们知道,在Linux系统中,所有的设备最终都是抽象成一个或者多个用户空间可以访问的设备文件,用户空间的进程通过这些设备文件的读写来达到控制硬件的目的。而这些设备文件都是由内核空间中的驱动程序创建、实现的。手机上的音频设备、接口比较多,对应的设备文件自然也比较多。对于播放声音或者录制声音来说,Audio HAL层是通过对PCM设备文件的读写来实现的。

二、查看PCM设备列表

设备上面可以查看到所有的音频设备。

dajun:/ # cd dev/snd/

那些以pcm打头的设备就是提供播放或录音的设备即本文要探讨的PCM设备,其他的设备提供效果、合成等功能。
音频设备的命名规则为 [device type]C[card index]D[device index][capture/playback],即名字中含有4部分的信息:

  1. device type
    设备类型,通常只有compr/hw/pcm这3种。从上图可以看到声卡会管理很多设备,PCM设备只是其中的一种设备。
  2. card index
    声卡的id,代表第几块声卡。通常都是0,代表第一块声卡。手机上通常都只有一块声卡。
  3. device index
    设备的id,代表这个设备是声卡上的第几个设备。设备的ID只和驱动中配置的DAI link的次序有关。如果驱动没有改变,那么这些ID就是固定的。
  4. capture/playback
    只有PCM设备才有这部分,只有c和p两种。c代表capture,说明这是一个提供录音的设备,p代表palyback,说明这是一个提供播放的设备。
    系统会在/proc/asound/pcm文件中列出所有的音频设备的信息,/proc/asound/pcm中的信息会更直观一些。
dajun:/ # cat proc/asound/pcm

对于普通的字符型设备,我们都是通过系统调用open/read/write/close来访问,有些设备支持随机访问,我们还可以使用lseek调用。PCM设备文件也是类似,不一样的是,我们可以使用open/close来打开/关闭设备,读取/写入文件却不是通过read/write,而都是通过ioctl来操作的。

(1)字符设备:提供连续的数据流,应用程序可以顺序读取,通常不支持随机存取。相反,此类设备支持按字节/字符来读写数据。举例来说,键盘、串口、调制解调器都是典型的字符设备。我们都是通过系统调用open/read/write/close来访问,有些设备支持随机访问,我们还可以使用lseek调用。
(2)块设备:应用程序可以随机访问设备数据,程序可自行确定读取数据的位置。硬盘、软盘、CD-ROM驱动器和闪存都是典型的块设备,应用程序可以寻址磁盘上的任何位置,并由此读取数据。此外,数据的读写只能以块(通常是512B)的倍数进行。与字符设备不同,块设备并不支持基于字符的寻址。

在Android Audio HAL层中,是通过TinyAlsa来访问PCM设备文件的。TinyAlsa封装了一系列接口用于PCM设备的访问,这些接口被Audio Hal调用以后,最终又会被Frameworks调用。接口包括:

struct pcm *pcm_open(unsigned int card, unsigned int device, unsinged int flags, struct pcm_config *config)
int pcm_close(struct pcm* pcm)
int pcm_write(struct pcm *pcm, const void* data, unsigned int count)
int pcm_read(struct pcm* pcm, void* data, unsigned int count)

从pcm_open这个接口可以看到,它通过几个参数获得了一个句柄,之后所有的操作都通过这个句柄来完成。这些参数里面,card代表第几块声卡,device就是上面提到的device index,它跟驱动中配置的DAI link的次序有关,flags参数中会指明这个设备是capture类型还是playback类型。通过这3个参数,就可以找到对应的PCM设备文件,例如 /dev/snd/pcmC0D5p,然后就可以去获取操作它的句柄,然后做更多的操作。

三、录音参数

1、AudioRecord

在android中采集音频的api是android.media.AudioRecord类
public AudioRecord (int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes)

  • audioSource

    the recording source. See MediaRecorder.AudioSource for recording source definitions.
    音频源:指的是从哪里采集音频。这里我们当然是从麦克风采集音频,所以此参数的值为MIC

  • sampleRateInHz

    the sample rate expressed in Hertz. Examples of rates are (but not limited to) 44100, 22050 and 11025.
    采样率:音频的采样频率,每秒钟能够采样的次数,采样率越高,音质越高。给出的实例是44100、22050、11025但不限于这几个参数。例如要采集低质量的音频就可以使用4000、8000等低采样率。

  • channelConfig

    describes the configuration of the audio channels. SeeCHANNEL_IN_MONO and CHANNEL_IN_STEREO
    声道设置:android支持双声道立体声和单声道。MONO单声道,STEREO立体声

  • audioFormat

    the format in which the audio data is represented. SeeENCODING_PCM_16BIT and ENCODING_PCM_8BIT
    编码制式和采样大小:采集来的数据当然使用PCM编码(脉冲代码调制编码,即PCM编码。PCM通过抽样、量化、编码三个步骤将连续变化的模拟信号转换为数字编码。) android支持的采样大小16bit 或者8bit。当然采样大小越大,那么信息量越多,音质也越高,现在主流的采样大小都是16bit,在低质量的语音传输的时候8bit 足够了。

  • bufferSizeInBytes

    the total size (in bytes) of the buffer where audio data is written to during the recording. New audio data can be read from this buffer in smaller chunks than this size. See getMinBufferSize(int, int, int) to determine the minimum required buffer size for the successful creation of an AudioRecord instance. Using values smaller than getMinBufferSize() will result in an initialization failure.
    采集数据需要的缓冲区的大小,如果不知道最小需要的大小可以在getMinBufferSize()查看。

2、关于音频采样率,音频帧率,每次采集多少字节的理解!

单声道:采样率1600016/81=32000bytes/second; 一分钟就是3200060=1920000bytes≈1.83MB。
双声道:采样率1600016/82=64000bytes/second; 一分钟就是64000
60=3840000bytes≈3.66MB。

解释:1600016/8160=1920000bytes≈1.83MB
采样频率采样精度(也叫位精度)/8声道
时间
采样频率:16000HZ
采样精度/8:16/8
声道:1
时间:60s

首先要理解一些音频处理的信息:采样率/位深度/通道数.

(1)采样率:

以秒为单位,每秒采集多少声音数据的频率.

(2)位深度:

上面我们说的采样率,每次会采集一次声音数据,这一次的声音数据的大小,既然是位深度,那么单位肯定是位了.

(3)通道数:

和硬件参数有关,采集声音源的设备有几个.

例:
采样率48000,位深度 16bit ,通道数2
知道这三个参数,那么基本我们就知道了
设备1秒内可以采集到多少音频数据是:
48000 16 2 = 1536000 位
48000 16 2 / 8 = 192000 字节.
也就是我的设备在一秒内可以采集192000
接下来音频的帧率,怎么理解呢,每秒内采集48000次,这个是总的采集次数,也许我们要分为100次,每次也就采集4800,或者分为50次,每次采集9600,。
这个就要看具体的硬件呢,所以硬件肯定会开放一个接口的.
让你获取一个minbufsize,意思就是这个,每一次提取多少字节,就是上面的参数缓冲区大小就是这个。

3、录音调试

tinyalsa工具使用即tinycap、tinyplay、tinymix及分析

(1)tinymix

tinymix可以查看系统的音频控件,可直接执行tinymix进行查看。tinymix更多的作用的是用来手动设置控件的值,控件可通过tinymix查看,或者通过mixer_paths.xml进行查看

(2)tinycap

tinycap是tinycap.cpp 编译出来的可执行文件,方便调试录音功能。
tinycap是用来录音的,使用之前也需要先设置录音相关的控件

tinycap test.wav  [-D card] [-d device] [-c channels] [-r rate] [-b bits] [-p period_size] [-n n_periods] [-T capture time]

使用命令前先用 tinymix 切换到音频通道
比如选通道0 tinymix 0

-D card 声卡
-d device 设备
-c channels 通道
-r rate 采样率
-b bits pcm 位宽
-p period_size 一次中断的帧数
-n n_periods 周期数

例子:tinycap /sdcard/test.pcm -D 0 -d 0 -c 2 -r 16000 -b 16 -p 768 -n 10
声卡0;设备0;二通道;16K采样率;16位位宽;一帧数据存储大小;采样n次

(3)tinyplay

参数与tinycap大体一样
可以使用tinyplay直接进行播放wav格式文件,在播放之前需要先使用tinymix进行相关控件的设置,

tinycap test.wav  [-D card] [-d device] [-p period_size] [-n n_periods]
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现 Android 多人语音通话中混音播放 PCM 的一种方法是使用 AudioTrack 类。首先,你需要将每个人的 PCM 数据进行混音,然后将混音后的 PCM 数据传递给 AudioTrack 对象进行播放。 以下是一些实现步骤: 1. 创建一个 AudioTrack 对象,并设置相关参数。 ``` int sampleRateInHz = 44100; int channelConfig = AudioFormat.CHANNEL_OUT_MONO; int audioFormat = AudioFormat.ENCODING_PCM_16BIT; int bufferSizeInBytes = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat); AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_VOICE_CALL, sampleRateInHz, channelConfig, audioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM); ``` 2. 将每个人的 PCM 数据进行混音。可以通过将每个人的 PCM 数据的每个采样点相加并除以参与混音的人数来实现混音。这里假设有两个人的 PCM 数据。 ``` byte[] pcmData1 = ...; // 第一个人的 PCM 数据 byte[] pcmData2 = ...; // 第二个人的 PCM 数据 byte[] mixPcmData = new byte[pcmData1.length]; for (int i = 0; i < pcmData1.length; i += 2) { // 将每个采样点相加并除以 2 short sample1 = (short) ((pcmData1[i + 1] << 8) | pcmData1[i]); short sample2 = (short) ((pcmData2[i + 1] << 8) | pcmData2[i]); short mixSample = (short) ((sample1 + sample2) / 2); mixPcmData[i] = (byte) (mixSample & 0xff); mixPcmData[i + 1] = (byte) ((mixSample >> 8) & 0xff); } ``` 3. 将混音后的 PCM 数据传递给 AudioTrack 对象进行播放。 ``` audioTrack.play(); audioTrack.write(mixPcmData, 0, mixPcmData.length); ``` 需要注意的是,在多人语音通话中,每个人的 PCM 数据的采样率、声道数、采样位数等参数可能不同,需要进行处理以确保混音后的 PCM 数据的参数一致。同时,由于混音后的 PCM 数据可能会比单个人的 PCM 数据更大,需要确保 AudioTrack 对象的缓冲区足够大以避免播放时出现丢失数据的情况。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值