简介
文章介绍
本文是对我在实际开发过程中的记录,记录了在Android中处理单乐器MusicXML文件,从中取出有用的音符并且将音符顺序还原为实际演奏顺序的音符数组的过程。主要内容有SAX解析,多音轨处理,重复音符处理,小段重复处理。
MusicXML
MusicXML(Music Extensible Markup Language 音乐扩展标记语言)是一个开放的基于XML 的音乐符号文件格式,MusicXML可以记录18世纪以来所有乐谱的展示和演奏细节。直白来讲,MusicXML本质上还是XML。
更详细的了解
MusicXML解析过程
获取MusicXML文件
- 本地:将MusicXML文件放入assets后,直接通过文件名称获取InputStream
InputStream is = getAssets().open(name);
- 远程:后台给出MusicXML文件的的URL后,先下载文件保存到sd卡
//下载具体操作
public static void downloadFile(String downloadUrl,String name) {
try {
Logger.d("url"+downloadUrl+" name:"+name+" event:"+event);
URL url = new URL(downloadUrl);
//打开连接
URLConnection conn = url.openConnection();
//打开输入流
InputStream is = conn.getInputStream();
//这是我自定义的文件夹 :Environment.getExternalStorageDirectory() + "/virtualPiano/"
String dirName = Constants.FILE_STORAGE_LOCATION;
File file = new File(dirName);
//不存在创建
if (!file.exists()) {
file.mkdirs();
}
//下载后的文件名
String fileName = dirName + name;
Logger.d(fileName);
File file1 = new File(fileName);
if (file1.exists()) {
file1.delete();
}
//创建字节流
byte[] bs = new byte[1024];
int len;
OutputStream os = new FileOutputStream(fileName);
//写数据
while ((len = is.read(bs)) != -1) {
os.write(bs, 0, len);
}
//完成后关闭流
Logger.d("download-finish");
os.close();
is.close();
} catch (Exception e) {
e.printStackTrace();
}
}
再通过文件名字获取文件
File dir = Constants.FILE_STORAGE_LOCATION;
File file = new File(dir, name);
SAX解析
对于XML文件的解析方式分为四种:1、DOM解析;2、SAX解析;3、JDOM解析;4、DOM4J解析。详细使用方法及对比
根据我们的需求,我们只读取数据且文件可能会比较大,所以我选择了SAX解析方式。
我们主要解析出的字段有
staff:音轨
chord:音符重复标识
repeat:小段重复
pitch:音符
alter:升降调,(音高组成之一,在pitch标签下)
step: 音阶(音高组成之一,在pitch标签下)
octave: 八度(音高组成之一,在pitch标签下)
duration:表示下一个音符距此音符的弹奏时间间隔
以上字段都会在 “note”标签里面,通过下面的readXML方法取解析一个xml文件或者文件流,最终会得到解析结果List<HashMap<String, String>>,里面每一个“note”都是一个hashMap,hashMap里面存储了一个note里面的所有内容。
public static List<HashMap<String, String>> readXML(File file)
{
try {
//实例化SAX工厂类
SAXParserFactory factory=SAXParserFactory.newInstance();
//实例化SAX解析器。
SAXParser sParser=factory.newSAXParser();
//实例化DefaultHandler,设置需要解析的节点
MyHandler myHandler=new MyHandler();
// 开始解析
sParser.parse(file, myHandler);
// 解析完成之后,关闭流
//inputStream.close(); //若使用inputStream在这里需要关闭流
//返回解析结果。
return myHandler.getList();
} catch (Exception e) {
e.printStackTrace();
// TODO: handle exception
}
return null;
}
public class MyHandler extends DefaultHandler {
private List<HashMap<String, String>> list = null; //解析后的XML内容
private HashMap<String, String> map = null; //存放当前需要记录的节点的XML内容
private String currentTag = null;//当前读取的XML节点
private String currentValue = null;//当前节点的XML文本值
private String nodeName = “note”;//需要解析的节点数组名称,这个是MusicXml里的包含音符等重要信息节点
@Override
public void startDocument() throws SAXException {
// 接收文档开始的通知。
// 实例化ArrayList用于存放解析XML后的数据
list = new ArrayList<HashMap<String, String>>();
}
@Override
public void startElement(String uri