android pcm编解码


一 什么是音频的采样率和采样大小 

自然界中的声音非常复杂,波形极其复杂,通常我们采用的是脉冲代码调制编码。即PCM编码。PCM通过抽样、量化、编码三个步骤将连续变化的模拟信号转换为数字编码。
抽样:在音频采集中叫做采样率。 由于声音其实是一种能量波,因此也有频率和振幅的特征,频率对应于时间轴线,振幅对应于电平轴线。波是无限光滑的,弦线可以看成由无数点组成,由于存储空间是相对有限的,数字编码过程中,必须对弦线的点进行采样。采样的过程就是抽取某点的频率值,很显然,在一秒中内抽取的点越多,获取得频率信息更丰富,为了复原波形,一次振动中,必须有2个点的采样,人耳能够感觉到的最高频率为20kHz,因此要满足人耳的听觉要求,则需要至少每秒进行40k次采样,用40kHz表达,这个40kHz就是采样率。我们常见的CD,采样率为44.1kHz。
 量化:我们这里的采样大小就是量化的过程, 将该频率的能量值并量化,用于表示信号强度。量化电平数为 2的整数次幂,我们常见的CD位16bit的采样大小,即2的16次方。  编码: 根据采样率和采样大小可以得知,相对自然界的信号,音频编码最多只能做到无限接近,至少目前的技术只能这样了,相对自然界的信号,任何数字音频编码方案都是有损的,因为无法完全还原。在计算机应用中,能够达到最高保真水平的就是PCM编码,被广泛用于素材保存及音乐欣赏,CD、DVD以及我们常见的WAV文件中均有应用。因此,PCM约定俗成了无损编码,因为PCM代表了数字音频中最佳的保真水准,并不意味着PCM就能够确保信号绝对保真,PCM也只能做到最大程度的无限接近。我们而习惯性的把MP3列入有损音频编码范畴,是相对PCM编码的。强调编码的相对性的有损和无损,是为了告诉大家,要做到真正的无损是困难的,就像用数字去表达圆周率,不管精度多高,也只是无限接近,而不是真正等于圆周率的值
为什么要使用音频压缩技术
 
要算一个PCM音频流的码率是一件很轻松的事情,采样率值×采样大小值×声道数bps。一个采样率为44.1KHz,采样大小为16bit,双声道的PCM编码的WAV文件,它的数据速率则为 44.1K×16×2 =1411.2 Kbps。我们常说128K的MP3,对应的WAV的参数,就是这个1411.2 Kbps,这个参数也被称为数据带宽,它和ADSL中的带宽是一个概念。将码率除以8,就可以得到这个WAV的数据速率,即176.4KB/s。这表示存储一秒钟采样率为44.1KHz,采样大小为16bit,双声道的PCM编码的音频信号,需要176.4KB的空间,1分钟则约为10.34M,这对大部分用户是不可接受的,尤其是喜欢在电脑上听音乐的朋友,要降低磁盘占用,只有2种方法,降低采样指标或者压缩。降低指标是不可取的,因此专家们研发了各种压缩方案。由于用途和针对的目标市场不一样,各种音频压缩编码所达到的音质和压缩比都不一样,在后面的文章中我们都会一一提到。有一点是可以肯定的,他们都压缩过。

频率与采样率的关系
 
采样率表示了每秒对原始信号采样的次数,我们常见到的音频文件采样率多为44.1KHz,这意味着什么呢?假设我们有2段正弦波信号,分别为20Hz和20KHz,长度均为一秒钟,以对应我们能听到的最低频和最高频,分别对这两段信号进行 40KHz的采样,我们可以得到一个什么样的结果呢?结果是:20Hz的信号每次振动被采样了40K/20=2000次,而20K的信号每次振动只有2次采样。显然,在相同的采样率下,记录低频的信息远比高频的详细。这也是为什么有些音响发烧友指责CD有数码声不够真实的原因,CD的44.1KHz采样也无法保证高频信号被较好记录。要较好的记录高频信号,看来需要更高的采样率,于是有些朋友在捕捉CD音轨的时候使用48KHz的采样率,这是不可取的!这其实对音质没有任何好处,对抓轨软件来说,保持和CD提供的44.1KHz一样的采样率才是最佳音质的保证之一,而不是去提高它。较高的采样率只有相对模拟信号的时候才有用,如果被采样的信号是数字的,请不要去尝试提高采样率。

流特征
 
随着网络的发展,人们对在线收听音乐提出了要求,因此也要求音频文件能够一边读一边播放,而不需要把这个文件全部读出后然后回放,这样就可以做到不用下载就可以实现收听了。也可以做到一边编码一边播放,正是这种特征,可以实现在线的直播,架设自己的数字广播电台成为了现实。  
 
 

二 android中AudioRecord采集音频的参数说明

 

在android中采集音频的api是android.media.AudioRecord类

其中构造器的几个参数就是标准的声音采集参数

以下是参数的含义解释

public AudioRecord (int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes)

Since: API Level 3

Class constructor.

Parameters

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. SeegetMinBufferSize(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()查看。

  // 音频获取源
private  int  audioSource = MediaRecorder.AudioSource.MIC;
// 设置音频采样率,44100是目前的标准,但是某些设备仍然支持22050,16000,11025
private  static  int  sampleRateInHz = 44100 ;
// 设置音频的录制的声道CHANNEL_IN_STEREO为双声道,CHANNEL_CONFIGURATION_MONO为单声道
private  static  int  channelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;
// 音频数据格式:PCM 16位每个样本。保证设备支持。PCM 8位每个样本。不一定能得到设备支持。
private  static  int  audioFormat = AudioFormat.ENCODING_PCM_16BIT;
复制代码
          File file = new File(Environment.getExternalStorageDirectory()
                        .getAbsolutePath() + "/test.pcm");
                // 删除录音文件
                if (file.exists())
                    file.delete();
                // 创建录音文件
                try {
                    file.createNewFile();
                } catch (IOException e) {
                    throw new IllegalStateException("Failed to create "
                            + file.toString());
                }
                try {
                    // Create a DataOuputStream to write the audio data into the
                    // saved file.
                    FileOutputStream fos = new FileOutputStream(file);// 建立一个可存取字节的文件
                    // Create a new AudioRecord object to record the audio.
                    // 获得满足条件的最小缓冲区大小
                    bufferSizeInBytes = AudioRecord.getMinBufferSize(
                            sampleRateInHz, channelConfig, audioFormat);
                    // 创建AudioRecord对象
                    audioRecord = new AudioRecord(audioSource, sampleRateInHz,
                            channelConfig, audioFormat, bufferSizeInBytes);
                    byte[] buffer = new byte[bufferSizeInBytes];
                    audioRecord.startRecording();
                    isRecording = true;
                    while (isRecording) {
                        audioRecord.read(buffer, 0, bufferSizeInBytes);
                        fos.write(buffer);
                    }
                    audioRecord.stop();
                    audioRecord.stop();
                    audioRecord.release();// 释放资源
                    audioRecord = null;
                    fos.close();
                } catch (Throwable t) {
                    Log.e("AudioRecord", "Recording Failed");
                }
复制代码
复制代码
// 放音的文件
                File file = new File(Environment.getExternalStorageDirectory()
                        .getAbsolutePath() + "/test.pcm");
                FileInputStream in = null;
                try {
                    in = new FileInputStream(file);
                } catch (FileNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                // 获得满足条件的最小缓冲区大小
                bufferSizeInBytes = AudioRecord.getMinBufferSize(
                        sampleRateInHz, channelConfig, audioFormat);
                byte[] buffer = new byte[bufferSizeInBytes];
                int byteread=0;
                AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
                        sampleRateInHz, channelConfig, audioFormat,
                        bufferSizeInBytes, AudioTrack.MODE_STREAM);
                // 放音
                audioTrack.play();
                try {
                    while ((byteread = in.read(buffer)) != -1) {
                        System.out.write(buffer, 0, byteread);
                        System.out.flush();
                        audioTrack.write(buffer, 0, bufferSizeInBytes);
                    }
                } catch (Exception e) {
                    Log.e("AudioTrack", "Playback Failed");
                }
复制代码

猜你喜欢

转载自blog.csdn.net/w1857518575/article/details/18085197
今日推荐