/// <summary> /// Calculates slur height trying to avoid collisions with stems /// </summary> /// <param name="note"></param> /// <param name="endPoint"></param> /// <returns></returns> private double DetermineSlurHeight(Note note, Slur slur, SlurInfo slurStartInfo, Point endPoint) { var notesUnderSlur = note.Staff.EnumerateUntilConditionMet <Note>(note, n => n.Slurs.FirstOrDefault(s => s.Number == slur.Number)?.Type == NoteSlurType.Start, true).ToArray(); if (notesUnderSlur.Length < 3) { return(10); } var notesUnderSlurExclusive = notesUnderSlur.Take(notesUnderSlur.Length - 1).Skip(1); var mostExtremePoint = GetMostExtremePoint(notesUnderSlurExclusive, slurStartInfo.StartPlacement); var angle = UsefulMath.BeamAngle(slurStartInfo.StartPoint.X, slurStartInfo.StartPoint.Y, endPoint.X, endPoint.Y); var slurYPositionInMostExtremePoint = slurStartInfo.StartPoint.TranslateHorizontallyAndMaintainAngle(angle, mostExtremePoint.X - slurStartInfo.StartPoint.X).Y; var mostExtremeYPosition = mostExtremePoint.Y; if (slurStartInfo.StartPlacement == VerticalPlacement.Above && mostExtremeYPosition < slurYPositionInMostExtremePoint) { return(Math.Abs(mostExtremeYPosition - slurYPositionInMostExtremePoint) + 10); } if (slurStartInfo.StartPlacement == VerticalPlacement.Below && mostExtremeYPosition > slurYPositionInMostExtremePoint) { return(Math.Abs(slurYPositionInMostExtremePoint - mostExtremeYPosition) + 10); } return(10); }
private PrintSuggestion CreateSuggestion(XElement element, MusicXmlParserState state, Staff staff) { PrintSuggestion suggestion = new PrintSuggestion(); element.IfAttribute("new-system").HasValue("yes").Then(() => suggestion.IsSystemBreak = true); element.IfAttribute("new-page").HasValue("yes").Then(() => suggestion.IsPageBreak = true); if (suggestion.IsPageBreak) { suggestion.IsSystemBreak = true; //Page breaks are treated also as system breaks. } element.IfDescendant("system-distance").Exists().Then(n => { suggestion.SystemDistance = UsefulMath.TryParse(n.Value); }); //Issue #45: Kopiujemy ustawienia z pierwszego staffa do wszystkich pozostałych staffów (bo są zdefiniowane tylko w pierwszym), innaczej się układ rozjedzie: if (!suggestion.SystemDistance.HasValue && staff != staff.Score.Staves.First()) { var thisStaffPrintSuggestions = staff.Elements.OfType <PrintSuggestion>().ToList(); var firstStaffPrintSuggestions = staff.Score.Staves.First().Elements.OfType <PrintSuggestion>().ToList(); var indexOfThisBreak = (thisStaffPrintSuggestions.Any() ? thisStaffPrintSuggestions.IndexOf(thisStaffPrintSuggestions.Last()) : -1) + 1; if (firstStaffPrintSuggestions.Count > indexOfThisBreak) { suggestion.SystemDistance = firstStaffPrintSuggestions[indexOfThisBreak].SystemDistance; } } element.IfDescendant("staff-distance").Exists().Then(n => { staff.Score.Pages.Last().DefaultStaffDistance = UsefulMath.TryParse(n.Value); }); return(suggestion); }
public override Model.Score Parse(string source) { var score = Score.CreateOneStaffScore(Clef.Treble, MajorScale.C); var splitted = source.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); foreach (var element in splitted) { if (element.Contains("_")) { var pitchAndDuration = element.Split(new[] { '_' }, StringSplitOptions.RemoveEmptyEntries); var midiPitch = int.Parse(pitchAndDuration[1]); var denominator = int.Parse(pitchAndDuration[0].Replace(".", "")); var numberOfNotes = pitchAndDuration[0].ToCharArray().Where(c => c == '.').Count(); var denominatorAsPowerOfTwo = UsefulMath.Log2(denominator); var note = Note.FromMidiPitch(midiPitch, new RhythmicDuration(denominatorAsPowerOfTwo, numberOfNotes)); score.FirstStaff.Elements.Add(note); } else if (element == "|") { score.FirstStaff.Elements.Add(new Barline()); } else { var denominator = int.Parse(element.Replace(".", "")); var numberOfNotes = element.ToCharArray().Where(c => c == '.').Count(); var denominatorAsPowerOfTwo = UsefulMath.Log2(denominator); var rest = new Rest(new RhythmicDuration(denominatorAsPowerOfTwo, numberOfNotes)); score.FirstStaff.Elements.Add(rest); } } return(score); }
public override void DrawArc(Primitives.Rectangle rect, double startAngle, double sweepAngle, Primitives.Pen pen, Model.MusicalSymbol owner) { Canvas.AppendLine("context.beginPath();"); Canvas.AppendLine(string.Format("context.arc({0},{1},{2},{3},{4});", rect.X.ToStringInvariant(), rect.Y.ToStringInvariant(), rect.Height.ToStringInvariant(), UsefulMath.GradToRadians(startAngle).ToStringInvariant(), UsefulMath.GradToRadians(sweepAngle).ToStringInvariant())); Canvas.AppendLine("context.stroke();"); }
public void CentsTest() { Assert.AreEqual(Proportion.Dupla.Cents, 1200); Assert.AreEqual(UsefulMath.CentsToLinear(1200), Proportion.Dupla.DoubleValue); var second1 = new Proportion(9, 8); var second2 = Proportion.GetApproximatedProportionFromCents(second1.Cents); Assert.AreEqual((int)second1.Cents, (int)second2.Cents); var pureFifth = Proportion.Sesquialtera.Cents; }
public override string ToString() { if (Score == null || !Score.Staves.Contains(this)) { string.Format("Staff (detached from score)."); } if (Part == null || !Part.Staves.Contains(this)) { return(string.Format("Staff ({0} in score).", UsefulMath.NumberToOrdinal(Score.Staves.IndexOf(this) + 1))); } return(string.Format("Staff {0} of part {1} ({2} in score).", Part.Staves.IndexOf(this) + 1, Part.PartId, UsefulMath.NumberToOrdinal(Score.Staves.IndexOf(this) + 1))); }
private static Tuple <Point, Point> GetBezierControlPoints(Point start, Point end, VerticalPlacement placement, double height) { var factor = placement == VerticalPlacement.Above ? -1 : 1; var angle = UsefulMath.BeamAngle(start.X, start.Y, end.X, end.Y); var angle2 = angle + (Math.PI / 2) * factor; var distance = Point.Distance(start, end); var startPointForControlPoints = start.TranslateByAngleOld(angle2, height); var control1 = startPointForControlPoints.TranslateByAngleOld(angle, distance * 0.25); var control2 = startPointForControlPoints.TranslateByAngleOld(angle, distance * 0.75); return(new Tuple <Point, Point>(control1, control2)); }
private static T?ParseValue <T>(string value) where T : struct { if (typeof(T) == typeof(int)) { return(UsefulMath.TryParseInt(value) as T?); } if (typeof(T) == typeof(double)) { return(UsefulMath.TryParse(value) as T?); } if (typeof(T) == typeof(DateTime)) { return(UsefulMath.TryParseDateTime(value) as T?); } throw new NotImplementedException("Type not supported"); }
private DateTime?GetDirectoryDate(string directory) { if (directory.Length < 14) { return(null); } var year = UsefulMath.TryParseInt(directory.Substring(0, 4)); if (!year.HasValue) { return(null); } var month = UsefulMath.TryParseInt(directory.Substring(4, 2)); if (!month.HasValue) { return(null); } var day = UsefulMath.TryParseInt(directory.Substring(6, 2)); if (!day.HasValue) { return(null); } var hour = UsefulMath.TryParseInt(directory.Substring(8, 2)); if (!hour.HasValue) { return(null); } var minute = UsefulMath.TryParseInt(directory.Substring(10, 2)); if (!minute.HasValue) { return(null); } var second = UsefulMath.TryParseInt(directory.Substring(12, 2)); if (!second.HasValue) { return(null); } return(new DateTime(year.Value, month.Value, day.Value).AddHours(hour.Value).AddMinutes(minute.Value).AddSeconds(second.Value)); }
/// <summary> /// Calculates beam angle /// </summary> /// <param name="beamStart">Beam start position</param> /// <param name="beamEnd">Beam end position</param> /// <returns></returns> public static double BeamAngle(Point beamStart, Point beamEnd) { return(UsefulMath.BeamAngle(beamStart.X, beamStart.Y, beamEnd.X, beamEnd.Y)); }
public override XDocument Parse(XDocument source) //TODO: Przetestować dokładnie i zrefaktorować { #if CSHTML5 throw new NotImplementedException("This method is not yet implemented for CSHTML5."); #else if (!NormalizeSpaceBeforeFirstNotesOfMeasures) { return(source); } if (source.Descendants(XName.Get("repeat")).Any()) { return(source); //Workaround - pomiń na razie te ze znakiem repetycji, bo się źle rysują } if (source.Descendants(XName.Get("time")).Count() > 1) { return(source); //Workaround - pomiń na razie te ze zmianami metrum, bo się źle rysują } foreach (XElement staffNode in source.Descendants(XName.Get("part"))) { // // FIRST PASS: Determine median note default-x // List <double> measureWidths = new List <double>(); List <double> spacesBeforeFirstNoteOfMeasure = new List <double>(); foreach (XElement measureNode in staffNode.Descendants(XName.Get("measure")).Skip(1)) //Don't remove space in first measure { //if (measureNode.Elements(XName.Get("barline")).Any()) continue; //Nie bierz pod uwagi tych ze znakami repetycji, itp. //TODO: Nie uwzględniać taktu, który następuje zaraz po zmianie metrum/klucza/znaków przykluczowych //Read widths of all measures: double?measureWidth = null; var widthAttribute = measureNode.Attributes().FirstOrDefault(a => a.Name == "width"); if (widthAttribute != null) { measureWidth = UsefulMath.TryParse(widthAttribute.Value); } if (measureWidth.HasValue) { measureWidths.Add(measureWidth.Value); } XElement firstNoteNode = measureNode.Descendants(XName.Get("note")).FirstOrDefault(); if (firstNoteNode != null) { double?noteDefaultX = null; var defaultXAttribute = firstNoteNode.Attributes().FirstOrDefault(a => a.Name == "default-x"); if (defaultXAttribute != null) { noteDefaultX = UsefulMath.TryParse(defaultXAttribute.Value); } if (noteDefaultX.HasValue) { spacesBeforeFirstNoteOfMeasure.Add(noteDefaultX.Value); } } } double medianNoteDefaultX = UsefulMath.Median(spacesBeforeFirstNoteOfMeasure.ToArray()); double averageNoteDefaultX = UsefulMath.Mean(spacesBeforeFirstNoteOfMeasure.ToArray()); // // SECOND PASS: Apply calculated median note default-x to all measures // //TODO: Nie uwzględniać taktu, który następuje zaraz po zmianie metrum/klucza/znaków przykluczowych foreach (XElement measureNode in staffNode.Descendants(XName.Get("measure")).Skip(1)) //Don't remove space in first measure { //if (measureNode.Elements(XName.Get("barline")).Any()) continue; //Nie bierz pod uwagi tych ze znakami repetycji, itp. double? currentValue = null; XElement firstNoteNode = measureNode.Descendants(XName.Get("note")).FirstOrDefault(); if (firstNoteNode == null) { continue; } var defaultXAttribute = firstNoteNode.Attributes().FirstOrDefault(a => a.Name == "default-x"); if (defaultXAttribute == null) { continue; } currentValue = UsefulMath.TryParse(defaultXAttribute.Value); if (!currentValue.HasValue) { continue; } double difference = currentValue.Value - medianNoteDefaultX; if (currentValue > averageNoteDefaultX) { foreach (var noteNode in measureNode.Descendants(XName.Get("note"))) { defaultXAttribute = noteNode.Attributes().FirstOrDefault(a => a.Name == "default-x"); if (defaultXAttribute == null) { continue; } currentValue = UsefulMath.TryParse(defaultXAttribute.Value); if (!currentValue.HasValue) { continue; } defaultXAttribute.Value = (currentValue.Value - difference).ToString(CultureInfo.InvariantCulture); } var widthAttribute = measureNode.Attributes().FirstOrDefault(a => a.Name == "width"); currentValue = UsefulMath.TryParse(widthAttribute.Value); if (!currentValue.HasValue) { continue; } if (widthAttribute != null) { widthAttribute.Value = (currentValue.Value - difference).ToString(CultureInfo.InvariantCulture); } } } } return(source); #endif }
public static IEnumerable <Note> AddUniformRhythm(this IEnumerable <Pitch> pitches, int duration) { var durationAsPowerOfTwo = UsefulMath.Log2(duration); return(AddUniformRhythm(pitches, new RhythmicDuration(durationAsPowerOfTwo))); }
static void Main() { Mandlebrot.MaxCount = 100; Console.SetWindowSize(Console.LargestWindowWidth - 10, Console.LargestWindowHeight - 10); zoom = (float)1 / 45; width = Console.WindowWidth; height = Console.WindowHeight - 1; ConsoleKey keyPressed; do { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { double xMin = -width * zoom * charAspect + xOffset; double xMax = width * zoom * charAspect + xOffset; double yMin = -height * zoom + yOffset; double yMax = height * zoom + yOffset; double a = UsefulMath.Map(0, width - 1, xMin, xMax, x); double b = UsefulMath.Map(0, height - 1, yMin, yMax, y); string charToWrite = Mandlebrot.InSet(Mandlebrot.MandlebrotCalc(a, b)) ? "o" : " "; Console.SetCursorPosition(x, y); Console.Write(charToWrite[0]); } } keyPressed = Console.ReadKey().Key; switch (keyPressed) { // Change Offsets case ConsoleKey.LeftArrow: xOffset -= 0.45f * zoom * 45; break; case ConsoleKey.RightArrow: xOffset += 0.45f * zoom * 45; break; case ConsoleKey.UpArrow: yOffset -= 0.45f * zoom * 45; break; case ConsoleKey.DownArrow: yOffset += 0.45f * zoom * 45; break; // Change zooms case ConsoleKey.PageUp: zoom /= 2; break; case ConsoleKey.PageDown: zoom *= 2; break; } } while (!keyPressed.Equals(ConsoleKey.Escape)); }
public override void ParseElement(MusicXmlParserState state, Staff staff, XElement element) { var builder = new NoteOrRestBuilder(state); element.IfAttribute("default-x").HasValue <double>().Then(m => builder.DefaultX = m); element.IfAttribute("measure").HasValue("yes").Then(m => builder.FullMeasure = true); element.IfAttribute("print-object").HasValue(new Dictionary <string, bool> { { "yes", true }, { "no", false } }).Then(m => builder.IsVisible = m); element.IfAttribute("size").HasValue(new Dictionary <string, NoteOrRestSize> { { "cue", NoteOrRestSize.Cue }, { "full", NoteOrRestSize.Full }, { "large", NoteOrRestSize.Large }, }).Then(s => builder.Size = s); element.IfElement("staff").HasValue <int>().Then(m => builder.Staff = staff.Part.Staves.ElementAt(m - 1)); element.IfElement("type").HasValue(new Dictionary <string, RhythmicDuration> { { "breve", RhythmicDuration.DoubleWhole }, { "whole", RhythmicDuration.Whole }, { "half", RhythmicDuration.Half }, { "quarter", RhythmicDuration.Quarter }, { "eighth", RhythmicDuration.Eighth }, { "16th", RhythmicDuration.Sixteenth }, { "32nd", RhythmicDuration.D32nd }, { "64th", RhythmicDuration.D64th }, { "128th", RhythmicDuration.D128th } }).Then(m => builder.BaseDuration = m); var typeElement = element.GetElement("type"); if (typeElement != null) { typeElement.IfAttribute("size").HasValue(new Dictionary <string, NoteOrRestSize> { { "cue", NoteOrRestSize.Cue }, { "full", NoteOrRestSize.Full }, { "large", NoteOrRestSize.Large }, }).Then(s => builder.Size = s); //"size" attribute apparently can be added to element "type" too } element.IfElement("voice").HasValue <int>().Then(m => builder.Voice = m); var graceElement = element.GetElement("grace"); if (graceElement != null) { graceElement.IfAttribute("slash").HasValue("yes") .Then(v => builder.GraceNoteType = GraceNoteType.Slashed) .Otherwise(v => builder.GraceNoteType = GraceNoteType.Simple); } element.IfElement("chord").Exists().Then(() => builder.IsChordElement = true); element.IfElement("accidental").HasValue("natural").Then(() => builder.HasNatural = true); element.IfElement("rest").Exists().Then(() => builder.IsRest = true); element.ForEachDescendant("dot", f => f.Exists().Then(() => builder.NumberOfDots++)); var pitchElement = element.IfElement("pitch").Exists().ThenReturnResult(); pitchElement.IfElement("step").HasAnyValue().Then(v => builder.Step = v); pitchElement.IfElement("octave").HasValue <int>().Then(v => builder.Octave = v); pitchElement.IfElement("alter").HasValue <int>().Then(v => builder.Alter = v); var unpitchedElement = element.IfElement("unpitched").Exists().Then(x => builder.IsUnpitched = true).AndReturnResult(); unpitchedElement.IfElement("display-step").HasAnyValue().Then(v => builder.Step = v); unpitchedElement.IfElement("display-octave").HasValue <int>().Then(v => builder.Octave = v); unpitchedElement.IfElement("display-alter").HasValue <int>().Then(v => builder.Alter = v); var tieElements = element.Elements().Where(e => e.Name == "tie"); foreach (var tieElement in tieElements) { tieElement.IfAttribute("type").HasValue("start").Then(v => { if (builder.TieType == NoteTieType.Stop) { builder.TieType = NoteTieType.StopAndStartAnother; } else { builder.TieType = NoteTieType.Start; } }).Otherwise(r => builder.TieType = NoteTieType.Stop); } element.IfElement("stem").HasValue("down") .Then(() => builder.StemDirection = VerticalDirection.Down) .Otherwise(r => builder.StemDirection = VerticalDirection.Up); element.GetElement("stem").IfAttribute("default-y").HasValue <float>().Then(v => { builder.StemDefaultY = v; builder.CustomStemEndPosition = true; }); element.ForEachDescendant("beam", h => h.HasValue(new Dictionary <string, NoteBeamType> { { "begin", NoteBeamType.Start }, { "end", NoteBeamType.End }, { "continue", NoteBeamType.Continue }, { "forward hook", NoteBeamType.ForwardHook }, { "backward hook", NoteBeamType.BackwardHook } }).Then(v => builder.BeamList.Add(v)) .Otherwise(r => { if (r.ToLowerInvariant() != "single") { throw new ScoreException(builder, $"Unsupported beam type \"{r}\"."); } })); var notationsNode = element.GetElement("notations"); var tupletNode = notationsNode.GetElement("tuplet"); tupletNode.IfAttribute("type").HasValue(new Dictionary <string, TupletType> { { "start", TupletType.Start }, { "stop", TupletType.Stop }, }).Then(v => builder.Tuplet = v); tupletNode.IfAttribute("placement").HasValue(new Dictionary <string, VerticalPlacement> { { "above", VerticalPlacement.Above }, { "below", VerticalPlacement.Below }, }).Then(v => builder.TupletPlacement = v); notationsNode.IfElement("fermata").Exists().Then(() => builder.HasFermataSign = true); notationsNode.IfElement("sound").Exists().Then(e => e.IfAttribute("dynamics").HasValue <int>().Then(v => state.CurrentDynamics = v)); notationsNode.IfHasElement("dynamics").Then(d => { var dir = new Direction(); d.IfAttribute("default-y").HasValue <int>().Then(v => { dir.DefaultYPosition = v; dir.Placement = DirectionPlacementType.Custom; }); d.IfAttribute("placement").HasValue(new Dictionary <string, DirectionPlacementType> { { "above", DirectionPlacementType.Above }, { "below", DirectionPlacementType.Below } }).Then(v => { if (dir.Placement != DirectionPlacementType.Custom) { dir.Placement = v; } }); foreach (XElement dynamicsType in d.Elements()) { dir.Text = dynamicsType.Name.LocalName; } staff.Elements.Add(dir); }); notationsNode.IfHasElement("articulations").Then(d => { d.IfElement("staccato").HasAnyValue().Then(() => builder.Articulation = ArticulationType.Staccato); d.IfElement("accent").HasAnyValue().Then(() => builder.Articulation = ArticulationType.Accent); d.IfAttribute("placement").HasValue(new Dictionary <string, VerticalPlacement> { { "above", VerticalPlacement.Above }, { "below", VerticalPlacement.Below }, }).Then(v => builder.ArticulationPlacement = v); }); var ornamentsNode = notationsNode.GetElement("ornaments"); ornamentsNode.GetElement("trill-mark").IfAttribute("placement").HasValue(new Dictionary <string, NoteTrillMark> { { "above", NoteTrillMark.Above }, { "below", NoteTrillMark.Below } }).Then(v => builder.TrillMark = v); ornamentsNode.IfElement("tremolo").HasValue <int>().Then(v => builder.TremoloLevel = v); var invMordentNode = ornamentsNode .IfElement("inverted-mordent") .Exists() .Then(e => builder.Mordent = new Mordent() { IsInverted = true }) .AndReturnResult(); invMordentNode.IfAttribute("placement").HasValue(new Dictionary <string, VerticalPlacement> { { "above", VerticalPlacement.Above }, { "below", VerticalPlacement.Below } }).Then(v => builder.Mordent.Placement = v); invMordentNode.IfAttribute("default-x").HasValue <double>().Then(v => builder.Mordent.DefaultXPosition = v); invMordentNode.IfAttribute("default-y").HasValue <double>().Then(v => builder.Mordent.DefaultYPosition = v); foreach (var slurNode in notationsNode?.Elements().Where(e => e.Name == "slur") ?? new XElement[] { }) { var slur = new Slur(); builder.Slurs.Add(slur); slurNode.IfAttribute("number").HasValue <int>().Then(v => slur.Number = v); slurNode.IfAttribute("type").HasValue(new Dictionary <string, NoteSlurType> { { "start", NoteSlurType.Start }, { "stop", NoteSlurType.Stop } }).Then(v => slur.Type = v); slurNode.IfAttribute("placement").HasValue(new Dictionary <string, VerticalPlacement> { { "above", VerticalPlacement.Above }, { "below", VerticalPlacement.Below } }).Then(v => slur.Placement = v); slurNode.IfAttribute("default-x").HasValue <double>().Then(v => slur.DefaultXPosition = v); slurNode.IfAttribute("default-y").HasValue <double>().Then(v => slur.DefaultYPosition = v); slurNode.IfAttribute("bezier-x").HasValue <double>().Then(v => slur.BezierX = v); slurNode.IfAttribute("bezier-y").HasValue <double>().Then(v => slur.BezierY = v); } foreach (var lNode in element.Elements().Where(n => n.Name == "lyric")) { //There can be more than one lyrics in one <lyrics> tag. Add lyrics to list once syllable type and text is set. //Then reset these tags so the next <syllabic> tag starts another lyric. Lyrics lyricsInstance = new Lyrics(); Lyrics.Syllable syllable = new Lyrics.Syllable(); bool isSylabicSet = false; bool isTextSet = false; var defaultYattribute = lNode.Attributes().FirstOrDefault(a => a.Name == "default-y"); if (defaultYattribute != null) { lyricsInstance.DefaultYPosition = UsefulMath.TryParse(defaultYattribute.Value); } foreach (XElement lyricAttribute in lNode.Elements()) { if (lyricAttribute.Name == "syllabic") { if (lyricAttribute.Value == "begin") { syllable.Type = SyllableType.Begin; } else if (lyricAttribute.Value == "middle") { syllable.Type = SyllableType.Middle; } else if (lyricAttribute.Value == "end") { syllable.Type = SyllableType.End; } else if (lyricAttribute.Value == "single") { syllable.Type = SyllableType.Single; } isSylabicSet = true; } else if (lyricAttribute.Name == "text") { syllable.Text = lyricAttribute.Value; isTextSet = true; } else if (lyricAttribute.Name == "elision") { syllable.ElisionMark = lyricAttribute.Value; } if (isSylabicSet && isTextSet) { lyricsInstance.Syllables.Add(syllable); syllable = new Lyrics.Syllable(); isSylabicSet = false; isTextSet = false; } } builder.Lyrics.Add(lyricsInstance); } if (builder.BeamList.Count == 0) { builder.BeamList.Add(NoteBeamType.Single); } var noteOrRest = builder.Build(); var correctStaff = noteOrRest.Staff ?? staff; correctStaff.Elements.Add(noteOrRest); }
/// <summary> /// Performs rebeaming with simple rules /// </summary> /// <param name="notes">Notes to rebeam</param> /// <param name="hookDirectionAlgorithm">Algorithm for determining hook direction in beams</param> /// <returns>Rebeamed notes</returns> public IEnumerable <NoteOrRest> Rebeam(IEnumerable <NoteOrRest> notes, HookDirectionAlgorithm hookDirectionAlgorithm = HookDirectionAlgorithm.ProductionCandidate) { Note previousNote = null; Note currentNote = null; Note nextNote = null; var noteArray = notes.ToArray(); for (int i = 0; i < noteArray.Length; i++) { previousNote = i == 0 ? null : noteArray[i - 1] as Note; currentNote = noteArray[i] as Note; nextNote = i == noteArray.Length - 1 ? null : noteArray[i + 1] as Note; currentNote.BeamList.Clear(); var numberOfBeams = currentNote.BaseDuration > RhythmicDuration.Eighth ? 0 : UsefulMath.Log2((int)currentNote.BaseDuration.Denominator) - 2; var numberOfBeamsOnPreviousNote = previousNote == null || previousNote.BaseDuration > RhythmicDuration.Eighth ? 0 : UsefulMath.Log2((int)previousNote.BaseDuration.Denominator) - 2; var numberOfBeamsOnNextNote = nextNote == null || nextNote.BaseDuration > RhythmicDuration.Eighth ? 0 : UsefulMath.Log2((int)nextNote.BaseDuration.Denominator) - 2; for (var b = 0; b < numberOfBeams; b++) { if (previousNote == null) { if (nextNote != null && numberOfBeamsOnNextNote < b + 1) { currentNote.BeamList.Add(DetermineHookDirection(notes.ToList(), currentNote, hookDirectionAlgorithm)); } else { currentNote.BeamList.Add(NoteBeamType.Start); } } else if (nextNote == null) { if (previousNote != null && numberOfBeamsOnPreviousNote < b + 1) { currentNote.BeamList.Add(DetermineHookDirection(notes.ToList(), currentNote, hookDirectionAlgorithm)); } else { currentNote.BeamList.Add(NoteBeamType.End); } } else { if (numberOfBeamsOnPreviousNote >= b + 1 && numberOfBeamsOnNextNote >= b + 1) { currentNote.BeamList.Add(NoteBeamType.Continue); } else if (numberOfBeamsOnPreviousNote < b + 1 && numberOfBeamsOnNextNote < b + 1) { currentNote.BeamList.Add(DetermineHookDirection(notes.ToList(), currentNote, hookDirectionAlgorithm)); } else if (numberOfBeamsOnPreviousNote < b + 1) { currentNote.BeamList.Add(NoteBeamType.Start); } else if (numberOfBeamsOnNextNote < b + 1) { currentNote.BeamList.Add(NoteBeamType.End); } } } } return(notes); }