WAV文件格式介绍
WAV文件遵守资源交换文件格式之规则,在文件的前44(或46)字节放置标头(header),使播放器或编辑器能够简单掌握文件的基本信息,其内容以区块(chunk)为最小单位,每一区块长度为4字节,而区块之上则由子区块包裹,每一子区块长度不拘,但须在前头先宣告标签及长度(字节)。标头的前3个区块记录文件格式及长度;接着第一个子区块包含8个区块,记录声道数量、采样率等信息;接着第二个子区块才是真正的音频资料,长度则视音频长度而定。内容如下表所示。须注意的是,每个区块的端序不尽相同,而音频内容本身则是采用小端序。
Android端使用java对wav进行数据读取,具体实现参考AudioReader的readHead()方法代码细节。
Android端实现读取音频文件源码介绍
主要使用Java io库中的 InputStream 接口,实现读取文件的字节流信息。主要实现类为AudioReader类,工具类为Utils,项目代码结构如下所示:
PO包的文件说明如下:
- AudioFragment为一个接口,存放0.975s音频文件的相关信息;
- IAudioFragment 为AudioFragment实现的接口;
- Score包含一个字符串(label)和一个浮点数(score),存放???;
- AudioReader为读取wav文件信息、预测音频标签的实现类;
- LabelsName存储着521个标签的字符串;
- MyComparator主要实现对Score对象数组的排序;
- Utils中包括几个常用的音频数据处理方法(工具类)。
IAudioFragment接口
该接口主要对功能进行初步定义。主要包含前N名的Score数组,以及实现是否在打鼾、咳嗽和打喷嚏。
public interface IAudioFragment { float start = 0; float end = 0; public Score[] scores = null; public abstract boolean isSnore(); public abstract boolean isCough(); public abstract boolean isSneeze(); public Score[] getScores(); public void setScores(Score[] scores);
}
AudioFragment实现类
对接口IAudioFragment进行实现,实现原理为:只保存前5的评分时,若Snore标签在前5中,则isSnore()返回true,否则返回false,其他方法同理。其中浮点数start和end表示该AudioFragment对象的起始与结束时间。例如:当你传入一段10s的音频进行预测时,会返回一个AudioFragment对象数组,其数组中第一个元素的起始时间为start = 0,结束时间为end = 0.975;第二个元素的起始时间为start =0.975,结束时间为end = 1.950(单位s)。
以下为部分核心代码:
float start = 0;
float end = 0;
Score[] scores = null;
Score scoreSnoring = null; // 打鼾 38
Score scoreCough = null; // 咳嗽 42
Score scoreSneeze = null; // 打喷嚏 44
······
@Override public boolean isSnore() { return isContain("Snore",scores);
}
@Override public boolean isCough() { return isContain("Cough",scores);
}
Score类
该对象只包含两个属性,将标签与评分绑定在一个对象中。
String label;
float score;
LabelsName类
主要存储521个标签的字符串数组,供其他类调用。
IAudioProcess接口
主要对音频的加载与处理功能的定义。
// 通过传入Android上下文环境,音频文件路径,获取该音频文件的输入流
public InputStream initInputStream(Context context, String fileName);
// 初始化Yamnet模型
public Yamnet initYamnetModel(Context context);
// 预测函数,该函数应该在传入音频文件后再调用,应该返回多个AudioFragment对象,AudioFragment对象中默认存储评分前5的标签
public AudioFragment[] predict();
// 同predict(),可以指存储评分中前topN个标签
public AudioFragment[] predict(int topN);
// 在初始化后使用,对目标fileName音频文件进预测,返回多个AudioFragment对象,AudioFragment对象中存储评分前topN的标签
public AudioFragment[] predictByAudioFile(String fileName,int topN);
// 预测0.975s音频数据的具体实现方法,Yamnet模型要求输入input为[-1,1]的长度为 15600的数组,经过预测得到评分结果,再与 start、end与topN一起用于构造一个 AudioFragment对象。
public AudioFragment predictOneSecond(Yamnet model, float[] input, float start, float end, int topN); // 0.975s
AudioReader类
主要对IAudioReader进行实现。其构造函数必须传入解析的文件名,以及上下文环境。以下为构造函数:
/**
* @param context Android Context
* @param fileName The target wav format file that needs to be predicted
* */
public AudioReader(Context context, String fileName){ this.context = context; this.fileName = fileName; initInputStream(context,fileName); getInstance(context);
}
在安卓活动中构建AudioReader对象并执行预测的示例如下:
(1)在初始化时直接指定文件并预测
AudioReader audioReader = new AudioReader(this,"demo.wav");
AudioFragment[] audioFragments = audioReader.predict(); // 默认只保存 前5评分
(2)在初始化时直接指定文件并预测前10个标签
AudioReader audioReader = new AudioReader(this,"demo.wav");
AudioFragment[] audioFragments = audioReader.predict(10);
(3)在初始化后改变预测文件
AudioReader audioReader = new AudioReader(this,"demo.wav");
// 预测other.wav中评分前10的标签
AudioFragment[] audioFragments = audioReader.predict("other.wav",10);
MyComprator类
主要对Score对象数组的排序,主要使用方法如下:
// resultScores 为Score对象数组,升序。在原有的resultScores上改变
Arrays.sort(resultScores, new MyComprator());
Utils类
主要完成重复性工作,例如Byte与int的转换、Byte与String的转换、Byte与int的转换,打印AudioFragment数组。