Click here to Skip to main content
15,867,756 members
Articles / Multimedia / GDI+

PSAM Control Library

Rate me:
Please Sign up or sign in to vote.
4.98/5 (71 votes)
19 Jul 2018BSD5 min read 140.5K   3.6K   95   64
WinForms library containing the IncipitViewer control for drawing musical notes

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:

psamcontrollibrary/psam.jpg

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:

C#
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.

C#
viewer.LoadFromXmlFile("example.xml"); 

The effect of the above code should look like that:

psamcontrollibrary/incipitviewerxml.png

To clear the staff, use the ClearMusicalIncipit() method:

C#
viewer.ClearMusicalIncipit(); 

We can also add notes and musical symbols programmatically. First we will add the G clef on line 2:

C#
Clef c = new Clef(ClefType.GClef, 2);
viewer.AddMusicalSymbol(c); 

Then we will add a new quarter note G:

C#
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:

C#
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:

psamcontrollibrary/incipitviewerbeams.png

And this is how the above example looks when all beams are set to NoteBeamType.Single:

psamcontrollibrary/incipitviewerbeamssingle.png

You can draw colored notes and musical symbols simply by changing their MusicalCharacterColor property:

psamcontrollibrary/colourfulnotes.png

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:

C#
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:

psamcontrollibrary/examplechords.png

The following example shows how to insert dots, rests and barlines:

C#
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:

psamcontrollibrary/exampledotrestbarline.png

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:

C#
viewer.PlayExternalMidiPlayer += 
	new IncipitViewer.PlayExternalMidiPlayerDelegate(viewer_PlayExternalMidiPlayer);

Create the following function for handling the event:

C#
void viewer_PlayExternalMidiPlayer(IncipitViewer sender)
{
    //Place your code here
} 

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:

C#
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:

C#
PrintDialog dlg = new PrintDialog();
dlg.Document = printDocument1;
if (dlg.ShowDialog() == DialogResult.OK)
{
     printDocument1.Print();
} 

Sample printout:

psamcontrollibrary/sample_print.png

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:

psamcontrollibrary/gibbons.png

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:

psamcontrollibrary/stem_example.png

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.

License

This article, along with any associated source code and files, is licensed under The BSD License


Written By
Poland Poland
I graduated from Adam Mickiewicz University in Poznań where I completed a MA degree in computer science (MA thesis: Analysis of Sound of Viola da Gamba and Human Voice and an Attempt of Comparison of Their Timbres Using Various Techniques of Digital Signal Analysis) and a bachelor degree in musicology (BA thesis: Continuity and Transitions in European Music Theory Illustrated by the Example of 3rd part of Zarlino's Institutioni Harmoniche and Bernhard's Tractatus Compositionis Augmentatus). I also graduated from a solo singing class in Fryderyk Chopin Musical School in Poznań. I'm a self-taught composer and a member of informal international group Vox Saeculorum, gathering composers, which common goal is to revive the old (mainly baroque) styles and composing traditions in contemporary written music. I'm the annual participant of International Summer School of Early Music in Lidzbark Warmiński.

Comments and Discussions

 
QuestionI can't add subsequent events. Pin
Member 1384386319-Sep-19 1:18
Member 1384386319-Sep-19 1:18 
AnswerRe: I can't add subsequent events. Pin
Marco Bertschi19-Sep-19 1:21
protectorMarco Bertschi19-Sep-19 1:21 
QuestionWhat is the current URL stated in this article? Pin
hgn18-Jan-17 17:08
hgn18-Jan-17 17:08 
AnswerRe: What is the current URL stated in this article? Pin
Ajcek8414-Jan-18 10:35
Ajcek8414-Jan-18 10:35 
Hi. I corrected the url.
QuestionSecond Stave in Incipit Viewer?? Pin
Member 1175616922-Aug-15 23:15
Member 1175616922-Aug-15 23:15 
AnswerRe: Second Stave in Incipit Viewer?? Pin
Ajcek8411-Sep-15 2:56
Ajcek8411-Sep-15 2:56 
QuestionPlayExternalMidiPlayer void example Pin
r3drogo22-Jul-15 19:06
r3drogo22-Jul-15 19:06 
Questionrelease new version? Pin
KoHHeKT31-May-15 5:28
KoHHeKT31-May-15 5:28 
AnswerRe: release new version? Pin
Ajcek847-Jun-16 10:39
Ajcek847-Jun-16 10:39 
Questionhow to integrate the PSAM control library Pin
Member 1156655512-Apr-15 23:23
Member 1156655512-Apr-15 23:23 
AnswerRe: how to integrate the PSAM control library Pin
tctom28-Apr-15 7:48
tctom28-Apr-15 7:48 
AnswerRe: how to integrate the PSAM control library Pin
Ajcek8419-Jul-15 7:12
Ajcek8419-Jul-15 7:12 
GeneralHaving Fun Making the Control Scalable Pin
tctom23-Jan-15 9:53
tctom23-Jan-15 9:53 
GeneralRe: Having Fun Making the Control Scalable Pin
Ajcek8419-Jul-15 7:06
Ajcek8419-Jul-15 7:06 
GeneralRe: Having Fun Making the Control Scalable Pin
ClockworkGolem25-Apr-16 16:05
ClockworkGolem25-Apr-16 16:05 
GeneralRe: Having Fun Making the Control Scalable Pin
r3drogo22-Jul-15 19:09
r3drogo22-Jul-15 19:09 
QuestionQuestion to Rest Pin
Robert Stefanz10-Dec-14 4:09
Robert Stefanz10-Dec-14 4:09 
AnswerRe: Question to Rest Pin
Ajcek844-Jan-15 22:39
Ajcek844-Jan-15 22:39 
QuestionMeaning of "bool print" argument? Pin
sigmundurus22-Aug-13 23:04
sigmundurus22-Aug-13 23:04 
AnswerRe: Meaning of "bool print" argument? Pin
Ajcek8423-Aug-13 1:55
Ajcek8423-Aug-13 1:55 
GeneralMy vote of 5 Pin
wmjordan21-Apr-13 15:07
professionalwmjordan21-Apr-13 15:07 
GeneralRe: My vote of 5 Pin
Ajcek8414-Jan-18 10:36
Ajcek8414-Jan-18 10:36 
Questionwhat font must install ? Pin
enochenoch2k29-Nov-11 15:55
enochenoch2k29-Nov-11 15:55 
AnswerRe: what font must install ? Pin
Ajcek8429-Nov-11 21:14
Ajcek8429-Nov-11 21:14 
GeneralRe: what font must install ? Pin
enochenoch2k30-Nov-11 2:17
enochenoch2k30-Nov-11 2:17 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.