/// <summary> /// Render a note. /// </summary> /// <param name="note">Information for the note to render</param> /// <returns>A note rendered on a Fraemwork Element</returns> private static FrameworkElement RenderNoteHead(Note note, double fontSize) { //todo: chords //todo: different shapes of note heads Panel noteHeadGrid = WPFRendering.CreateAutoSizingGrid(); FrameworkElement noteHead = new Label(); FrameworkElement noteHeadInside = new Label(); if (string.IsNullOrEmpty(note.color)) note.color = Constants.Colors.DEFAULT_NOTE_COLOR; Brush noteBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString(note.color)); RotateTransform hollowRT = new RotateTransform(); bool rotateHead = false; bool hollow = false; double noteHeadHeight = WPFRendering.GetFontFraction(12, fontSize); double noteHeadWidth = noteHeadHeight * 1.5; switch (note.type.Value) { //todo: size note heads using width expected for font case NoteTypeValue.whole: // make outside // todo: made this into a function noteHead = new Ellipse() { Fill = noteBrush, Height = noteHeadHeight, Width = noteHeadWidth }; hollow = true; // make hole noteHeadInside = new Ellipse() { Fill = new SolidColorBrush(Colors.White), Height = noteHeadHeight * 0.6, Width = noteHeadWidth / 2 }; hollowRT.Angle = Constants.Note.HeadRotations.WHOLE_NOTE_HOLLOW; //todo: make this rotation a constant, note same as head rotation break; case NoteTypeValue.half: // make outside noteHead = new Ellipse() { Fill = noteBrush, Height = noteHeadHeight, Width = noteHeadWidth }; rotateHead = true; hollow = true; // make hole noteHeadInside = new Ellipse() { Fill = new SolidColorBrush(Colors.White), Height = noteHeadHeight * 0.7, Width = noteHeadWidth * .8 }; hollowRT.Angle = Constants.Note.HeadRotations.HALF_NOTE_HOLLOW; //todo: make this rotation a constant, note same as head rotation break; case NoteTypeValue.quarter: noteHead = new Ellipse() { Fill = noteBrush, Height = noteHeadHeight, Width = noteHeadWidth }; rotateHead = true; break; case NoteTypeValue.eighth: noteHead = new Ellipse() { Fill = noteBrush, Height = noteHeadHeight, Width = noteHeadWidth }; rotateHead = true; break; case NoteTypeValue.Item16th: noteHead = new Ellipse() { Fill = noteBrush, Height = noteHeadHeight, Width = noteHeadWidth }; rotateHead = true; break; case NoteTypeValue.Item32nd: noteHead = new Ellipse() { Fill = noteBrush, Height = noteHeadHeight, Width = noteHeadWidth }; rotateHead = true; break; case NoteTypeValue.Item64th: noteHead = new Ellipse() { Fill = noteBrush, Height = noteHeadHeight, Width = noteHeadWidth }; rotateHead = true; break; case NoteTypeValue.Item128th: noteHead = new Ellipse() { Fill = noteBrush, Height = noteHeadHeight, Width = noteHeadWidth }; rotateHead = true; break; case NoteTypeValue.Item256th: rotateHead = true; break; case NoteTypeValue.Item512th: rotateHead = true; break; } // perform head rotation and add to the notehead grid if (rotateHead) RotateNoteHead(noteHead); noteHeadGrid.Children.Add(noteHead); // if it is hallow, add the hallow center if (hollow) { noteHeadInside.LayoutTransform = hollowRT; noteHeadGrid.Children.Add(noteHeadInside); } WPFRendering.RecalculateSize(noteHead); WPFRendering.RecalculateSize(noteHeadGrid); //todo: Render modifiers return noteHeadGrid; }
/// <summary> /// Render a rest /// </summary> /// <param name="note">Representation of the rest to render</param> /// <returns>The rest rendered on a Framework Element</returns> private static FrameworkElement RenderRest(Note note, double fontSize) { string restChar = ""; switch (note.type.Value) { case NoteTypeValue.whole: restChar = Constants.RestCharacters.WHOLE_REST; break; case NoteTypeValue.half: restChar = Constants.RestCharacters.HALF_REST; break; case NoteTypeValue.quarter: restChar = Constants.RestCharacters.QUARTER_REST; break; case NoteTypeValue.eighth: restChar = Constants.RestCharacters.EIGHTH_REST; break; case NoteTypeValue.Item16th: restChar = Constants.RestCharacters.SIXTEETH_REST; break; case NoteTypeValue.Item32nd: restChar = Constants.RestCharacters.THIRTYSECOND_REST; break; case NoteTypeValue.Item64th: restChar = Constants.RestCharacters.SIXTYFOURTH_REST; break; case NoteTypeValue.Item128th: restChar = Constants.RestCharacters.ONETWENTYEIGHTH_REST; break; case NoteTypeValue.Item256th: restChar = "!"; break; case NoteTypeValue.Item512th: restChar = "!"; break; } //todo: it is a rest FrameworkElement restLabel = WPFRendering.GetMusicalLabel(restChar, fontSize); WPFRendering.RecalculateSize(restLabel); return restLabel; }
//todo: put note rendering into its own class /// <summary> /// Parse the contents of a note and prepare the basic label /// </summary> /// <param name="note">The note to parse and create a label for</param> /// <param name="fontSize">The size of the font being used</param> /// <param name="clefSign">The sign of the clef for this note. Default is G</param> /// <returns>A label with the note in it</returns> public static Panel RenderNoteOrRest(Note note, double fontSize, ClefSign clefSign = ClefSign.G) { double yOffset = WPFRendering.GetFontFraction(-2, fontSize); //offset cause not exactly in right spot //todo: Render appropriate connectors //todo: render barline //todo: tuplets, triplets, beams //todo: likely remove this whole function because will need to draw own notes due to stem when beaming String noteChar = Constants.Note.Characters.WHOLE_NOTE; Panel grid = WPFRendering.CreateAutoSizingGrid(); //todo: if it has a rest, then parse as rest, otherwise parse as note bool isRest = note.Items.SingleOrDefault(i => i.GetType() == typeof(Rest)) != null; if (!isRest) { FrameworkElement noteHead = RenderNoteHead(note, fontSize); noteHead.Margin = new Thickness(noteHead.Margin.Left, noteHead.Margin.Top + yOffset, noteHead.Margin.Right, noteHead.Margin.Bottom); grid.Children.Add(noteHead); //todo: stems when beamed... This will likely require stems to be drawn after the note heads of the beaming // however, beaming is not supposed to go over measures so this will save some of the problems that this could have //get pitch object Pitch pitch = (Pitch)note.Items.SingleOrDefault(t => t.GetType() == typeof (Pitch)); if (pitch != null) { //todo: make into function //move grid up or down appropriately Pitch clefDefaultPitch = GetDefaultPitch(clefSign); int stepDifference = clefDefaultPitch.step - pitch.step + (int.Parse(Constants.Note.TrebelDefaults.PITCH.octave) - int.Parse(pitch.octave)) * 8; if(pitch.step == Step.A || pitch.step == Step.B) { stepDifference -= 7; } // todo: octave var difference = (int.Parse(pitch.octave) - int.Parse(clefDefaultPitch.octave)); if (difference != 0) { difference *= 7; stepDifference += (difference < 0) ? difference : -difference; } const double stepDistance = 5.5; double topOffset = WPFRendering.GetFontFraction(39, fontSize); double marginTop = noteHead.Margin.Top + (stepDifference * WPFRendering.GetFontFraction(stepDistance, fontSize)) + topOffset; // create and add note stem System.Windows.Shapes.Line noteStem = new System.Windows.Shapes.Line(); if (note.stem.Value != stemvalue.none) noteStem = CreateNoteStem(yOffset, note, noteHead, fontSize); grid.Children.Add(noteStem); // add the note head noteHead.Margin = new Thickness(noteHead.Margin.Left, marginTop, noteHead.Margin.Right, noteHead.Margin.Bottom); noteStem.Margin = new Thickness(noteStem.Margin.Left, marginTop, noteStem.Margin.Right, noteStem.Margin.Bottom); //debug: remove Console.Out.WriteLine("default: " + Constants.Note.TrebelDefaults.PITCH.step + " " + pitch.step + pitch.octave + " " + stepDifference); } // render ledger line if necessary if(LineThroughNoteHead(pitch, clefSign)) { FrameworkElement ledgerLine = RenderLedgerLine(noteHead, fontSize); grid.Children.Add(ledgerLine); } //todo: accidentals //todo: clef change on note //todo: other notes? //todo: make into function //todo: if is above half of the staff, rotate so everything points down // rotate head /* RotateTransform rt = new RotateTransform(180); rt.CenterX = noteHead.ActualWidth / 2; rt.CenterY = 10 * 3 / 4; //todo: use note height calculation from render notehead grid.RenderTransform = rt; */ } else { FrameworkElement restElement = RenderRest(note, fontSize); restElement.Margin = new Thickness(0, -WPFRendering.GetFontHeight(fontSize, Constants.MusicFonts.DEFAULT) / 4, 0, 0); grid.Children.Add(restElement); } //grid.VerticalAlignment = VerticalAlignment.Top; WPFRendering.RecalculateSize(grid); return grid; }
private static System.Windows.Shapes.Line CreateNoteStem(double yOffset, Note note, FrameworkElement noteHead, double fontSize) { if (String.IsNullOrEmpty(note.stem.color)) note.stem.color = Constants.Colors.DEFAULT_NOTE_COLOR; double stemHeight = WPFRendering.GetFontHeight(fontSize, Constants.MusicFonts.DEFAULT) * 2.5 / 5; double y1 = 0; double y2 = 0; if (note.stem.Value == stemvalue.up) { y1 = yOffset + noteHead.ActualHeight / 2; y2 = yOffset + noteHead.ActualHeight / 2 - stemHeight; } else if (note.stem.Value == stemvalue.down) { y1 = yOffset + noteHead.ActualHeight / 2; y2 = yOffset + noteHead.ActualHeight / 2 + stemHeight; } else if (note.stem.Value == stemvalue.@double) { //TODO: figure this out } return new System.Windows.Shapes.Line() { X1 = noteHead.ActualWidth - WPFRendering.GetFontFraction(Constants.Note.STEM_X_OFFSET, fontSize), X2 = noteHead.ActualWidth - WPFRendering.GetFontFraction(Constants.Note.STEM_X_OFFSET, fontSize), Y1 = y1, Y2 = y2, StrokeThickness = CalculateLineWidth(fontSize), Stroke = new SolidColorBrush((Color)ColorConverter.ConvertFromString(note.stem.color)) }; }
public void ParseSimpleOneNoteStringTest() { String testString1 = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" + "<!DOCTYPE score-partwise PUBLIC\n" + "\"-//Recordare//DTD MusicXML 3.0 Partwise//EN\"\n" + "\"http://www.musicxml.org/dtds/partwise.dtd\">\n" + "<score-partwise version=\"3.0\">\n" + "<part-list><score-part id=\"P1\"><part-name>Music</part-name></score-part></part-list>" + "<part id=\"P1\"><measure number=\"1\"><attributes><divisions>1</divisions><key>" + "<fifths>0</fifths></key><time><beats>4</beats><beat-type>4</beat-type></time>" + "<clef><sign>G</sign><line>2</line></clef></attributes>" + "<note><pitch><step>C</step><octave>4</octave></pitch><duration>4</duration>" + "<type>whole</type><stem>up</stem></note></measure></part></score-partwise>"; // create the expected structure Note n = new Note() { type = new NoteType { Value = NoteTypeValue.whole }, stem = new Stem { Value = stemvalue.up }, Items = new object[] { new Pitch { step = Step.C, octave = "4" }, new decimal(4) }, ItemsElementName = new[] { ItemsChoiceType1.pitch, ItemsChoiceType1.duration } }; ScorePartwisePartMeasure m = new ScorePartwisePartMeasure() { number = "1", Items = new object[]{ new Attributes() { divisions = 1, key = new[] { new Key { Items = new object[] { 0 }, ItemsElementName = new[] { ItemsChoiceType8.fifths }, } }, time = new[] { new Time { Beats = "4", BeatType = "4"} }, clef = new[] { new Clef { sign = ClefSign.G, line = "2" } } }, n } }; // set expected ScorePartwise expected = new ScorePartwise { version = "3.0", part = new ScorePartwisePart[1] }; // measure part and information expected.part[0] = new ScorePartwisePart { measure = new ScorePartwisePartMeasure[1] }; expected.part[0].measure[0] = m; expected.part[0].id = "P1"; // part list expected.partList = new PartList() { scorepart = new ScorePart() { id = "P1", partName = new PartName { Value = "Music" } } }; ScorePartwise actual = ScorePartwise.Deserialize(testString1); CompareProperties(actual.GetType(), actual, expected); }
public static bool LoadFromFile(string fileName, out Note obj) { Exception exception = null; return LoadFromFile(fileName, out obj, out exception); }
/// <summary> /// Deserializes xml markup from file into an note object /// </summary> /// <param name = "fileName">string xml file to load and deserialize</param> /// <param name = "obj">Output note object</param> /// <param name = "exception">output Exception value if deserialize failed</param> /// <returns>true if this XmlSerializer can deserialize the object; otherwise, false</returns> public static bool LoadFromFile(string fileName, out Note obj, out Exception exception) { exception = null; obj = default(Note); try { obj = LoadFromFile(fileName); return true; } catch (Exception ex) { exception = ex; return false; } }
public static bool Deserialize(string xml, out Note obj) { Exception exception = null; return Deserialize(xml, out obj, out exception); }
/// <summary> /// Deserializes workflow markup into an note object /// </summary> /// <param name = "xml">string workflow markup to deserialize</param> /// <param name = "obj">Output note object</param> /// <param name = "exception">output Exception value if deserialize failed</param> /// <returns>true if this XmlSerializer can deserialize the object; otherwise, false</returns> public static bool Deserialize(string xml, out Note obj, out Exception exception) { exception = null; obj = default(Note); try { obj = Deserialize(xml); return true; } catch (Exception ex) { exception = ex; return false; } }