数字音频基本概念
在实现功能之前,我们先来了解一下数字音频的有关属性:
采样频率(Sample Rate):每秒采集声音的数量,它用赫兹(Hz)来表示。(采样率越高越靠近原声音的波形)
采样精度(Bit Depth):指记录声音的动态范围,它以位(Bit)为单位。(声音的幅度差)
声音通道(Channel):声道数。比如左声道右声道。
采样量化后的音频最终是一串数字,声音的大小(幅度)会体现在这个每个数字数值大小上;而声音的高低(频率)和声音的音色(Timbre)都和时间维度有关,会体现在数字之间的差异上。
音频的编码与解码
自然界中的声音非常复杂,波形极其复杂,通常我们采用的是脉冲代码调制编码,即PCM编码。PCM通过抽样、量化、编码三个步骤将连续变化的模拟信号转换为数字编码。本篇文章介绍的混音就是对PCM数据做处理。
相对自然界的信号,任何数字音频编码方案都是有损的,因为无法完全还原。在计算机应用中,能够达到最高保真水平的就是PCM编码,平时常见的WAV文件就是在PCM数据前加上一个44字节的RIFF头部组成的。
音频信号在时域和频域上具有相关性,也即存在数据冗余,音频编码的实质是减少音频中的冗余。
那么,解码的目的就是让编码后的数据恢复成PCM源数据。
AAC,Mp3 --> Decoder --> Audio PCM Data
常见的音频编码格式有以下这些:
public static final String MIMETYPE_AUDIO_AMR_NB = "audio/3gpp";
public static final String MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb";
public static final String MIMETYPE_AUDIO_MPEG = "audio/mpeg";
public static final String MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
public static final String MIMETYPE_AUDIO_QCELP = "audio/qcelp";
public static final String MIMETYPE_AUDIO_VORBIS = "audio/vorbis";
public static final String MIMETYPE_AUDIO_OPUS = "audio/opus";
public static final String MIMETYPE_AUDIO_G711_ALAW = "audio/g711-alaw";
public static final String MIMETYPE_AUDIO_G711_MLAW = "audio/g711-mlaw";
public static final String MIMETYPE_AUDIO_RAW = "audio/raw";
public static final String MIMETYPE_AUDIO_FLAC = "audio/flac";
public static final String MIMETYPE_AUDIO_MSGSM = "audio/gsm";
public static final String MIMETYPE_AUDIO_AC3 = "audio/ac3";
public static final String MIMETYPE_AUDIO_EAC3 = "audio/eac3";
音频的混音
音频混音的原理: 量化的语音信号的叠加等价于空气中声波的叠加。
反应到音频数据上,也就是把同一个声道的数值进行简单的相加,但是这样同时会产生一个问题,那就是相加的结果可能会溢出,当然为了解决这个问题已经有很多方案了,在这里我们采用简单的平均算法。
下面的演示程序适用于音频文件采样率、通道数、采样精度一样的情况:
/**
* 混合音频,使用平均算法
*
* @param mixedBytes 输出混合后的数据到该byte数组
* @param shorts1 需要混合的short数组1
* @param shorts2 需要混合的short数组2
*/
private void mixRawAudioBytes(byte[] mixedBytes, short[] shorts1, short[] shorts2) {
for (int i = 0; i < shorts2.length; i++) {
shorts1[i] = (short) ((shorts2[i] + shorts1[i]) / 2);
}
for (int i = 0; i < shorts1.length; i++) {
mixedBytes[i * 2] = (byte) (shorts1[i] & 0x00FF);
mixedBytes[i * 2 + 1] = (byte) ((shorts1[i] & 0xFF00) >> 8);
}
}