Introduction
UPDATE: This article describes an old .NET project which eventually evolved into bigger open source framework Manufaktura.Controls. You can read about it here: https://www.codeproject.com/Articles/1252423/Music-Notation-in-NET
PSAM Control Library is a WinForms library containing the IncipitViewer
control for drawing musical notes which can be read from MusicXml file or added programmatically. The library was initially a component of larger software Polish System for Archivising Music (http://www.archiwistykamuzyczna.pl/?lang=en) but I thought it could be useful for other software developers, so I decided to distribute it under BSD licence. PSAM Control Library is written in C# under Microsoft Visual Studio Express.
The following screen illustrates the use of many IncipitViewer
controls in a manuscript database application:
PSAM Control Library official website is available at http://musicengravingcontrols.com/.
Using the Code
IncipitViewer
control requires a special font to draw notes and other musical symbols. You can create your own font or use the included font Polihymnia which is based on Ben Laenen's Euterpe font and distributed under Sil Open Font Licence. Of course you have to install the font in your fonts directory to display notes properly.
The simplest way to add IncipitViewer
control to your project is to drag and drop the PSAMControlLibrary.dll file to your Toolbox
and then drag and drop the IncipitViewer
control to your form. You can also create an IncipitViewer
control programmatically, for example:
IncipitViewer viewer = new IncipitViewer();
viewer.Dock = DockStyle.Fill;
Controls.Add(viewer);
Remember to add using <code>PSAMControlLibrary;
directive to your code.
To read music from MusicXml file use LoadFromXmlFile(string fileName)
method and as an argument, type the path of the XML file that you wish to open. Remember that only the first stave is supported - other staves will be skipped.
viewer.LoadFromXmlFile("example.xml");
The effect of the above code should look like that:
To clear the staff, use the ClearMusicalIncipit()
method:
viewer.ClearMusicalIncipit();
We can also add notes and musical symbols programmatically. First we will add the G clef on line 2:
Clef c = new Clef(ClefType.GClef, 2);
viewer.AddMusicalSymbol(c);
Then we will add a new quarter note G:
Note n = new Note("G", 0, 4, MusicalSymbolDuration.Quarter,
NoteStemDirection.Up, NoteTieType.None,
new List<NoteBeamType>() {NoteBeamType.Single});
viewer.AddMusicalSymbol(n);
The first argument of Note constructor is a string
representing one of the following names of steps: A, B, C, D, E, F, G. The second argument is number of sharps (positive number) or flats (negative number) where 0 means no alteration. The third argument is the number of an octave. The next arguments are: duration of the note, stem direction and type of tie (NoteTieType.None
if the note is not tied). The last argument is a list of beams. If the note doesn't have any beams, it must still have that list with just one element NoteBeamType.Single
(even if duration of the note is greater than eighth). To make it clear how beamlists work, let's try to add a group of two beamed sixteenths and eighth:
Note s1 = new Note("A", 0, 4, MusicalSymbolDuration.Sixteenth,
NoteStemDirection.Down, NoteTieType.None,
new List<NoteBeamType>() { NoteBeamType.Start, NoteBeamType.Start});
Note s2 = new Note("C", 1, 5, MusicalSymbolDuration.Sixteenth,
NoteStemDirection.Down, NoteTieType.None,
new List<NoteBeamType>() { NoteBeamType.Continue, NoteBeamType.End });
Note e = new Note("D", 0, 5, MusicalSymbolDuration.Eighth,
NoteStemDirection.Down, NoteTieType.None,
new List<NoteBeamType>() { NoteBeamType.End });
viewer.AddMusicalSymbol(s1);
viewer.AddMusicalSymbol(s2);
viewer.AddMusicalSymbol(e);
The results should look like this:
And this is how the above example looks when all beams are set to NoteBeamType.Single
:
You can draw colored notes and musical symbols simply by changing their MusicalCharacterColor
property:
Although IncipitViewer
does not support multiple staves, it supports chords because they are part of many monophonic instruments' idiom. If IsChordElement
property of a note is set to true
, a note is treated as a chord element of the preceding note:
Note n1 = new Note("C", 0, 4, MusicalSymbolDuration.Half,
NoteStemDirection.Up, NoteTieType.None,
new List<NoteBeamType>() { NoteBeamType.Single });
Note n2 = new Note("E", 0, 4, MusicalSymbolDuration.Half,
NoteStemDirection.Up, NoteTieType.None,
new List<NoteBeamType>() { NoteBeamType.Single });
Note n3 = new Note("G", 0, 4, MusicalSymbolDuration.Half,
NoteStemDirection.Up, NoteTieType.None,
new List<NoteBeamType>() { NoteBeamType.Single });
n2.IsChordElement = true;
n3.IsChordElement = true;
viewer.AddMusicalSymbol(n1);
viewer.AddMusicalSymbol(n2);
viewer.AddMusicalSymbol(n3);
Result:
The following example shows how to insert dots, rests and barlines:
Note n4 = new Note("A", 0, 4, MusicalSymbolDuration.Half,
NoteStemDirection.Up, NoteTieType.None,
new List<NoteBeamType>() { NoteBeamType.Single });
n4.NumberOfDots = 1;
Rest r = new Rest(MusicalSymbolDuration.Quarter);
Barline b = new Barline();
viewer.AddMusicalSymbol(n4);
viewer.AddMusicalSymbol(r);
viewer.AddMusicalSymbol(b);
Result:
When the mouse is over the control, two buttons appear in the upper right corner: the first saves the MusicXml file associated with the control and the second invokes OnPlayExternalMidiPlayer
event handler. You can subscribe PlayExternalMidiPlayer
event:
viewer.PlayExternalMidiPlayer +=
new IncipitViewer.PlayExternalMidiPlayerDelegate(viewer_PlayExternalMidiPlayer);
Create the following function for handling the event:
void viewer_PlayExternalMidiPlayer(IncipitViewer sender)
{
}
In the above function, you can place a code to read notes from the control and use your own functions or another library to play them. To access the desired musical symbol from the control, use IncipitViewer.IncipitElement(int i)
method where i
is the index of the element. To convert a note to midi pitch, you can use a MusicalSymbol.ToMidiPitch (string step, int alter, int octave)
method.
To print the content of IncipitViewer
control, create a PrintDocument
object and subscribe its PrintPage
event:
private void printDocument1_PrintPage(object sender,
System.Drawing.Printing.PrintPageEventArgs e)
{
Graphics g = e.Graphics;
viewer.DrawViewer(g, true);
}
Then print the document using the Print()
method:
PrintDialog dlg = new PrintDialog();
dlg.Document = printDocument1;
if (dlg.ShowDialog() == DialogResult.OK)
{
printDocument1.Print();
}
Sample printout:
Points of Interest
A curious thing connected with graphic layout of the score is the problem of determining the proper length of stems under beams. In 17th and 18th century printed music stems of equal length were very often in use which consequence was "breaking" of beams. Scores looked something like that:
In contemporary music engraving however, beams must be straight. Unfortunately straight beams lead to variable length of stems and the proper length of stems must be determined programmatically. The following image demonstrates all values which we need to accomplish this:
To determine the location of stem ending, we must find out the length of segment y1, which is a vertical distance between the stem ending of the last note in group and the stem ending of the note which interests us. Because:
tg alpha = y<sub>1</sub>/x<sub>1</sub>
then:
y<sub>1</sub> = x<sub>1</sub> * tg alpha
Value of tg alpha is the ratio of the vertical distance between the stem endings of the first and last note in group and the horizontal distance between the stem endings of the first and last note in group (we assume that we know both values):
tg alpha = y / x
So:
y<sub>1</sub> = x<sub>1</sub> * (y / x)
y1 is the vertical coordinate of the point of stem ending of the note that interests us.
History
In version 2.1.0.2, I added colored notes and made some changes in IncipitViewer
class in order to allow porting this library to WPF. A WPF version of this library is available HERE.