PSAM Control Library是一个.NETWinForms库,它包含了一个名为IncipitViewer的控件,用于绘制可以从MusicXml文件读取或以编程方式添加的音乐符号。最初,这个库是波兰音乐档案系统()的一部分,但作者认为它对其他软件开发者也有用,因此决定在BSD许可证下分发。PSAM Control Library是用C#在Microsoft Visual Studio Express下编写的。
以下是在手稿数据库应用程序中使用多个IncipitViewer控件的屏幕截图:
PSAM Control Library的官方网站可以在找到。
要使用IncipitViewer控件,需要一个特殊的字体来绘制音符和其他音乐符号。可以创建自己的字体或使用包含的Polihymnia字体,它基于Ben Laenen的Euterpe字体,并在Sil Open Font Licence下分发。当然,必须在字体目录中安装字体以正确显示音符。
将IncipitViewer控件添加到项目中最简单的方法是将PSAMControlLibrary.dll文件拖放到工具箱中,然后将IncipitViewer控件拖放到表单上。也可以以编程方式创建一个IncipitViewer控件,例如:
C# IncipitViewer viewer = new IncipitViewer(); viewer.Dock = DockStyle.Fill; Controls.Add(viewer);
记得在代码中添加using PSAMControlLibrary;指令。
要读取MusicXml文件中的音乐,使用LoadFromXmlFile(string fileName)方法,并作为参数输入想要打开的XML文件的路径。请记住,只支持第一行,其他行将被跳过。
C# viewer.LoadFromXmlFile("example.xml");
上述代码的效果应该是这样的:
要清除五线谱,使用ClearMusicalIncipit()方法:
C# viewer.ClearMusicalIncipit();
还可以以编程方式添加音符和音乐符号。首先,在第二行添加G谱号:
C# Clef c = new Clef(ClefType.GClef, 2); viewer.AddMusicalSymbol(c);
然后添加一个新的四分音符G:
C# Note n = new Note("G", 0, 4, MusicalSymbolDuration.Quarter, NoteStemDirection.Up, NoteTieType.None, new List() {NoteBeamType.Single}); viewer.AddMusicalSymbol(n);
音符构造函数的第一个参数是一个字符串,代表以下音名之一:A, B, C, D, E, F, G。第二个参数是升号(正数)或降号(负数)的数量,其中0表示没有变化。第三个参数是八度数。接下来的参数是:音符的持续时间,音符干的方向和连音类型(如果音符没有连音,则为NoteTieType.None)。最后一个参数是一个连音列表。如果音符没有任何连音,它仍然必须有一个只有一个元素NoteBeamType.Single的列表(即使音符的持续时间大于八分音符)。为了清楚地说明连音列表的工作原理,让尝试添加一组两个连音的十六分音符和八分音符:
C# Note s1 = new Note("A", 0, 4, MusicalSymbolDuration.Sixteenth, NoteStemDirection.Down, NoteTieType.None, new List() { NoteBeamType.Start, NoteBeamType.Start}); Note s2 = new Note("C", 1, 5, MusicalSymbolDuration.Sixteenth, NoteStemDirection.Down, NoteTieType.None, new List() { NoteBeamType.Continue, NoteBeamType.End }); Note e = new Note("D", 0, 5, MusicalSymbolDuration.Eighth, NoteStemDirection.Down, NoteTieType.None, new List() { NoteBeamType.End }); viewer.AddMusicalSymbol(s1); viewer.AddMusicalSymbol(s2); viewer.AddMusicalSymbol(e);
结果应该像这样:
这是当所有连音都设置为NoteBeamType.Single时上述示例的样子:
可以通过改变它们的MusicalCharacterColor属性来简单地绘制彩色音符和音乐符号:
尽管IncipitViewer不支持多行,但它支持和弦,因为它们是许多单音乐器习语的一部分。如果一个音符的IsChordElement属性设置为true,那么这个音符就被视为前一个音符的和弦元素:
C# Note n1 = new Note("C", 0, 4, MusicalSymbolDuration.Half, NoteStemDirection.Up, NoteTieType.None, new List() { NoteBeamType.Single }); Note n2 = new Note("E", 0, 4, MusicalSymbolDuration.Half, NoteStemDirection.Up, NoteTieType.None, new List() { NoteBeamType.Single }); Note n3 = new Note("G", 0, 4, MusicalSymbolDuration.Half, NoteStemDirection.Up, NoteTieType.None, new List() { NoteBeamType.Single }); n2.IsChordElement = true; n3.IsChordElement = true; viewer.AddMusicalSymbol(n1); viewer.AddMusicalSymbol(n2); viewer.AddMusicalSymbol(n3);
结果如下:
以下示例展示了如何插入点、休止符和节拍线:
C# Note n4 = new Note("A", 0, 4, MusicalSymbolDuration.Half, NoteStemDirection.Up, NoteTieType.None, new List() { NoteBeamType.Single }); n4.NumberOfDots = 1; Rest r = new Rest(MusicalSymbolDuration.Quarter); Barline b = new Barline(); viewer.AddMusicalSymbol(n4); viewer.AddMusicalSymbol(r); viewer.AddMusicalSymbol(b);
结果如下:
当鼠标悬停在控件上时,右上角会出现两个按钮:第一个保存与控件关联的MusicXml文件,第二个调用OnPlayExternalMidiPlayer事件处理程序。可以订阅PlayExternalMidiPlayer事件:
C# viewer.PlayExternalMidiPlayer += new IncipitViewer.PlayExternalMidiPlayerDelegate(viewer_PlayExternalMidiPlayer);
为处理事件创建以下函数:
C# void viewer_PlayExternalMidiPlayer(IncipitViewer sender) { // Place your code here }
在上面的函数中,可以放置代码从控件中读取音符,并使用自己的函数或另一个库来播放它们。要访问控件中所需的音乐符号,请使用IncipitViewer.IncipitElement(int i)方法,其中i是元素的索引。要将音符转换为MIDI音高,可以使用MusicalSymbol.ToMidiPitch(string step, int alter, int octave)方法。
要打印IncipitViewer控件的内容,请创建一个PrintDocument对象并订阅其PrintPage事件:
C# private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e) { Graphics g = e.Graphics; viewer.DrawViewer(g, true); }
然后使用Print()方法打印文档:
C# PrintDialog dlg = new PrintDialog(); dlg.Document = printDocument1; if (dlg.ShowDialog() == DialogResult.OK) { printDocument1.Print(); }
示例打印输出:
与乐谱的图形布局相关的一个有趣的事情是确定连音下音符干的正确长度的问题。在17世纪和18世纪的印刷音乐中,经常使用等长的音符干,其结果是“断裂”的连音。乐谱看起来像这样:
然而,在当代音乐雕刻中,连音必须是直的。不幸的是,直的连音导致音符干的长度变化,必须以编程方式确定音符干的正确长度。以下图片展示了需要完成此操作的所有值:
要确定音符干末端的位置,必须找出段y1的长度,这是组中最后一个音符的音符干末端与感兴趣的音符的音符干末端之间的垂直距离。因为:
tg alpha = y1/x1
那么:
y1 = x1 * tg alpha
tg alpha的值是组中第一个和最后一个音符的音符干末端之间的垂直距离与第一个和最后一个音符的音符干末端之间的水平距离的比率(假设知道这两个值):
tg alpha = y/x
所以:
y1 = x1 * (y/x)