iOS中MusicXML的解析和使用
MusicXML
维基上MusicXML的解释是:(英语:Music Extensible Markup Language,音乐扩展标记语言)是一个开放的基于XML的记录西式乐谱的文件格式。该格式是完全自由、开放记录的,并依据W3C社区的许可协议自由使用。
简单的来说,MusicXML其实就是XML,只不过承载的是乐谱信息的XML。在我们需要绘制或者创建六线谱之类的时候,可以通过MusicXML来记录我们需要绘制或者创建的信息。目的是为音乐符号创造一种通用格式。(本文是基于MusicXML 3.0和2.0,所以可能会有一些出入。)因为项目中用到的是吉他谱,就以吉他谱的含义来进行说明和解释。
一 文件解析
MusicXML本质也是XML,所以文件上的解析可以按XML的解析来。
XML的解析在iOS里面有两种解析方式,一个SAX解析,一个DOM解析。
方式一:SAX解析
SAX解析的特点是逐行进行解析。也就是一边读取一边处理。对于大文件,极大的提高了解析效率。
方式二:DOM解析
DOM解析的特点是通过节点解析。需要一次性读取整个XML文件并形成一个节点树,通过遍历树结构可以检索任意XML节点,读取它的属性和值。
比如第三方:GDataXMLNode
在这里我用的SAX解析,具体可以去看下这里,是我之前用来解析MusicXML文档写的。
- 注意点:因为是使用的utf8编码,所以一旦XML里面出现了其他的编码方式,就有可能造成解析失败,所以在解析前最好将xml处理一遍。
二 属性解析
MusicXML本质上还是XML,所以文件结构是和XML一样的,这个是XML文档所需要的XML声明。声明用的xml版本是1.0和xml传输数据的时候的字符编码。(假如文档里有中文,编码方式不是UTF-8,显示上就会乱码)
<?xml version="1.0" encoding="UTF-8" ?>
这里是说明当前使用的MusicXML版本是什么,标签里面包含的内容就是MusicXML的内容
<score-partwise version =“ 3.0”>
...
</score-partwise>
score-partwise元素里面的有几个大元素:
-
work元素,work-title里面内容是这个xml的名字
<work> <work-title>Aloha oe</work-title> </work>
-
identification,这里是文档的一些编辑信息,比如编辑日期,是用什么软件编辑的
<identification> <encoding> <encoding-date>2019-10-08</encoding-date> <software>Guitar Pro 7.0.9</software> <encoder></encoder> </encoding> </identification>
-
defaults,这里是一些页面的宽高信息
<defaults> <scaling> <millimeters>6.4</millimeters> <tenths>40</tenths> </scaling> <page-layout> <page-height>1850</page-height> <page-width>1310</page-width> </page-layout> </defaults>
-
part-list,这里面是一些乐器类型,MIDI信息
<part-list> <score-part id="P1"> <part-name>Ukulele</part-name> <part-abbreviation>uke.</part-abbreviation> <midi-instrument id="P1"> <midi-channel>1</midi-channel> <midi-bank>1</midi-bank> <midi-program>25</midi-program> <volume>80</volume> <pan>0</pan> </midi-instrument> </score-part>
-
part,这里面是音符信息,默认是只有一个part的,所以大多数MusicXML里面是只有一个part元素,比如:
<score-partwise> <part id="P1"> ... </part> </score-partwise>
但有可能会出现多个part,具体看制谱的时候是怎么做的,比如: <score-partwise> <part id="P1"> ... </part> <part id="P2"> ... </part> </score-partwise>
-
measure元素,part里面是小节measure元素,比如一首谱子有24个小节,那么就会有24个measure,measure里面的属性number等于多少是可以自己定义的,比如从0开始,比如从1开始。measure里面包含了一个小节里面所有的信息。
- attributes,先看measure里面第一个元素attributes,一般出现在第一个小节中,它里面是该乐谱的基本信息。
-
divisions,divisions表示的是一个四分音符的时值,每个小节可能有不同的值,比如说在第一个小节中
<divisions>1</divisions>
表示的是当前小节中一个四分音符的时值是1个单位,divisions是要配合note中的duration看的,比如在4/4的谱子中,下面这个四分音符note的duration就是1,
<note> <duration>1</duration> <type>quarter</type> </note>
而下面这个全音符的duration就是4,因为4/4是以四分音符为一拍,每小节四拍。所以全音符的话时值相当于四个四分音符,所以它的duration就是4。
<note> <duration>4</duration> <type>whole</type> </note>
-
再比如如果当前音符存在附点,附点的时值是当前音符的一半,因为按上下文当前divisions还是1,而在当前出现了附点,divisions要变为2,所以在接下来会变成下面这样
``` <divisions>2</divisions> <note> <dot/> <duration>3</duration> <type>quarter</type> </note> ```
再比如如果当前小节存在十六分音符:
``` <divisions>4</divisions> <note> <duration>1</duration> <type>16th</type> </note> ``` 1. key,key元素表示乐谱的升降号情况,fifths为0表示不升不降,一般正数表示升调的个数,负数表示降调的个数,这个元素还有一个属性`mode/type`来表示此调为大调(major)还是小调。 ``` <key> <fifths>0</fifths> <mode>major</mode> </key> ``` 2. time,time元素里面是这首曲子拍速等一些相关的信息,比如下面的含义就是以8分音符为一拍,每小节6拍。 ``` <time> <beats>6</beats>//每个小节有几个拍 <beat-type>8</beat-type>//以哪种音符为一拍 </time> ``` 3. clef,clef里面是谱号信息。 ``` <clef number="1"> <sign>TAB</sign> <line>5</line> </clef> ``` 4. staff-details,staff-details这里是空弦的时候每根弦的音名。 *staff-lines,staff-lines元素指定了行数
- harmony,和弦图元素
- note,然后接下来是<note>标签,这里面是每个音符的一些信息。
-
chord,这个元素可以理解为和弦,叠音,也就是说当这个标签出现的时候,竖直方向是有多个音的,用叠音描述会形象一些,也就是几个音叠在一起;相反如果没有这个元素,可以大致认为当前这个音符是根音,也就是在最底部的音。比如:
<note> <chord/> <type>quarter</type> <stem>up</stem> <notations> <technical> <string>4</string> <fret>2</fret> </technical> </notations> </note>
-
rest,这个元素是休止符的表示,比如下面表示一个二分休止符:
<note> <rest/> <duration>2</duration> <type>half</type> </note>
duration,上面divisions元素中已经说过了,表示当前音符的时值,也就是这个音符的持续时间。具体值是通过divisions来计算。
-
type,表示当前音符是什么类型,比如<type>half</type>,表示的是个二分音符。type里面的值是有固定的,大致是这些
- whole:全音符,也就是占一个小节
- half:二分音符
- quarter:四分音符
- eighth:八分音符
- 16th:16分音符
- 32th:32分音符
如果出现了更小的音符,基本就是以数字+th为名字。
-
pitch,音高,里面是音、调的一些信息。
step:表示是abcdefg哪个调。
alter:表示升降,作为音高的一部分,-2或2表示重降和重升。
-
octave:表示在哪个八度上。
比如:<pitch> <step>F</step> <alter>-2</alter> <octave>4</octave> </pitch>
stem:符干。
voice:声部。
-
dot:附点,表示当前音符的时值增加一半。比如下面例子,某个小节中的一个四分音符,如果divisions为2的话,它的时值duration是3。divisions表示的,可以理解成一个四分音符的时值。那么当前这个小节中的四分音符加个附点的话,它时值就是一个四分的1.5倍,也就是3。
<note> <duration>3</duration> <type>quarter</type> <dot/> </note>
-
tie:表示一个连音的开始和结束。可能会存在多个,有个number属性可以用来区分或者标识具体的tie元素。
比如下面表示一个连音:<note> <tie type="start"/> </note> <note> <tie type="stop"/> <tie type="start"/> </note> <note> <tie type="stop"/> </note>
-
lyric:歌词。
<note> <lyric> <syllabic>begin</syllabic> <text>我爱的</text>//这里是具体的歌词内容 </lyric> </note>
-
beam:桥梁,可能存在多个,用来显示八音符,十六分音符底部横梁等。
<note> <beam number="1">begin</beam> </note> <note> <beam number="1">continue</beam> <beam number="2">begin</beam> </note> <note> <beam number="1">continue</beam> <beam number="2">end</beam> </note>
-
notations:里面包含的是这个音符具体的一些信息,比如几弦几品,是什么音之类的信息。具体是下面这些元素:
-
brush:扫弦。因为用的GuitarPro7软件制谱,所以GP7导出的xml里面扫弦是导出是长下面这样的,其他软件不大确定。
- type:扫弦的方向,
*down
说明是向上扫弦,箭头向上;
*up
说明是向下扫弦,箭头向下。
<notations> <?GP7 <root><brush type="down"/></root>?> </notations>
- type:扫弦的方向,
-
arpeggiate:琶音。
- direction:琶音方向,
*up
是向上琶音,箭头向上。
*down
是向下琶音,箭头向下。
<notations> <arpeggiate direction="up"/> </notations>
- direction:琶音方向,
-
tuplet:三连音,基本能遇到的就两种吧? 一个是: 八分音符样子的三个,就是一拍的三连音;另外一个是:四分音符样子的三个,就是两排拍的三连音,和beats,beat-type无关。
- type:来标识三连音的开始和结束,start,stop。
- number:来区别哪个三连音。
- placement:支架朝向above-below。
- bracket:是否显示支架 yes-no。
-
-
- attributes,先看measure里面第一个元素attributes,一般出现在第一个小节中,它里面是该乐谱的基本信息。
```
<notations>
<tuplet type="start" number="1" bracket="yes" placement="below"/>
<notations>
```
* slide:滑弦,开始音品数大于终止音品数,则是下滑音,否则是上滑音。
* type:滑音的开始或者结束,start,stop。
* number:标识。
* slur:连接符,这里一般特指滑音或者击勾弦的连接符,实际使用中貌似可以忽略它的存在,因为slide或者hammer-on元素里面的type属性已经能完整的告诉我们他们的开始和结束了。可能我还没遇到slur的特定情况吧。
* type:连接的开始或者结束。
```
<notations>
<slur type="start"/>//连接符
<slide number="5" type="start"/>
</notations>
```
* tied:这里的tied元素含义和外层tie元素意思是一样的,都是用来表示连音的开始和结束。
```
<notations>
<tied type="start"/>
</notations>
```
* technical:这个元素里面是几弦几品,手指,推弦,泛音等一些信息。
```
<technical>
<string>3</string>
<fret>2</fret>
<fingering>2</fingering>
<pluck></pluck>
</technical>
```
* string:告诉我们这个音符是几弦。
* fret:告诉我们这个音符是几品。
* fingering:告诉我们按哪个手指。
* pluck:左手哪根手指。
* bend:推弦,推全音或者推半音
* bend_alter:推半音还是全音标识,全音2,半音1,回原位0
* isRelease:推弦回原位标识,有这个标识是会原位,否则不会。
<technical>
<bend>
<bend-alter>2</bend-alter>
</bend>
<bend>
<bend-alter>0</bend-alter>
<release/>
</bend>
</technical>
* harmonic,natural:泛音。
```
<technical>
<harmonic>
<natural/>
<base-pitch/>
</harmonic>
</technical>
```
* hammer-on:击勾弦,用的是同一个字段,<fret>品数由小到大是击弦,品数由大到小是勾弦。也可以直接根据hammer-on元素中的内容判断,是`H`基本上就是击弦;勾弦的话内容就是`P`的标识。
* number:标识。
* type:击勾弦的开始结束,start,stop。
比如下面这个,是一个击弦,
```
<note>
<notations>
<technical>
<hammer-on number="1" type="start">H</hammer-on>
<string>2</string>
<fret>0</fret>
</technical>
<slur type="start"/>
</notations>
</note>
<note>
<notations>
<technical>
<hammer-on number="1" type="stop"/>
<string>2</string>
<fret>2</fret>
</technical>
<slur type="stop"/>
</notations>
</note>
```
基本能用到的大多数就是这么多了,更多详细的可以查看MusicXML的官网文档
三 (六线谱)吉他谱绘制
绘制的话,看具体的需求了,可以尝试在layer上异步绘制,然后在需要的情况下再渲染到view上。比如用CAShapeLayer
,轻量,性能又好。