/// <summary> /// Adds the note to the staff in the correct location /// </summary> /// <param name="nr">The note to add</param> private void addNoteOrRestToStaff(NoteOrRest nr) { // Get the currently selected note, rest, or staff fragment var elem = Viewer.SelectedElement; if (elem == null) { //MessageBox.Show("You must select a staff to write to or a note to insert after."); //return; elem = model.Data.FirstStaff.Elements[0]; } //If user selects the staff itself, append to last note. Else insert after selected note. //int staffIndex = model.Data.Staves.IndexOf(elem.Staff); // Get current staff **PROBABLY NOT NEEDED** if (elem.GetType().IsSubclassOf(typeof(NoteOrRest))) { // Not staff fragment, so insert note after selected element int index = elem.Staff.Elements.IndexOf(elem); elem.Staff.Elements.Insert(index + 1, nr); elem.Measure.Elements.Insert(elem.Measure.Elements.IndexOf(elem) + 1, nr); model.fitMeasure(elem.Measure, model.TimeSig); } else { elem.Staff.Elements.Add(nr); // Staff fragment, so add note to end of staff model.fitMeasure(nr.Measure, model.TimeSig); } // Trigger an update in the viewmodel model.updateView(); }
public static void TupletMark(IMeasurementService measurementService, IScoreService scoreService, ScoreRendererBase renderer, NoteOrRest element, int beamLoop) { if (measurementService.TupletState == null) { throw new Exception("DrawTupletMark was called but no tuplet is currently open in staff."); } Staff staff = scoreService.CurrentStaff; NoteOrRest firstElementInTuplet = staff.Peek <NoteOrRest>(element, PeekType.BeginningOfTuplet); int index = staff.Elements.IndexOf(firstElementInTuplet); List <MusicalSymbol> elementsUnderTuplet = staff.Elements.GetRange(index, staff.Elements.IndexOf(element) - index); var elementsUnderTupletForAverageStemLength = elementsUnderTuplet.OfType <Note>().Where(n => MusicalSymbol.DirectionToPlacement(n.StemDirection) == measurementService.TupletState.TupletPlacement).ToList(); double averageStemLength = elementsUnderTupletForAverageStemLength.Count == 0 ? 0 : elementsUnderTupletForAverageStemLength.Average(n => n.ActualStemLength); averageStemLength += 10; //Add space int placementMod = measurementService.TupletState.TupletPlacement == VerticalPlacement.Above ? -1 : 1; double tupletBracketStartXPosition = firstElementInTuplet.TextBlockLocation.X + 6; double tupletBracketStartYPosition = firstElementInTuplet.TextBlockLocation.Y + 25 + averageStemLength * placementMod; double tupletBracketEndXPosition = element.TextBlockLocation.X + 12; double tupletBracketEndYPosition = element.TextBlockLocation.Y + 25 + averageStemLength * placementMod; if (measurementService.TupletState.AreSingleBeamsPresentUnderTuplet) //Draw tuplet bracket { renderer.DrawLine(new Point(tupletBracketStartXPosition, tupletBracketStartYPosition), new Point(tupletBracketEndXPosition, tupletBracketEndYPosition), element); renderer.DrawLine(new Point(tupletBracketStartXPosition, tupletBracketStartYPosition), new Point(tupletBracketStartXPosition, firstElementInTuplet.TextBlockLocation.Y + 25 + (averageStemLength - 4) * placementMod), element); renderer.DrawLine(new Point(tupletBracketEndXPosition, tupletBracketEndYPosition), new Point(tupletBracketEndXPosition, element.TextBlockLocation.Y + 25 + (averageStemLength - 4) * placementMod), element); } double numberOfNotesYTranslation = 0; if (measurementService.TupletState.TupletPlacement == VerticalPlacement.Above) { numberOfNotesYTranslation -= 18; //If text should appear above the tuplet, move a bit to up } //If bracket is not drawn, move up or down to fill space if (!measurementService.TupletState.AreSingleBeamsPresentUnderTuplet) { numberOfNotesYTranslation += 10 * (measurementService.TupletState.TupletPlacement == VerticalPlacement.Above ? 1 : -1); } var allElementsUnderTuplet = elementsUnderTuplet.OfType <NoteOrRest>().ToList(); allElementsUnderTuplet.Add(element); var tupletNumber = CalculateTupletNumber(allElementsUnderTuplet); renderer.DrawString(Convert.ToString(tupletNumber), MusicFontStyles.LyricsFont, new Point(tupletBracketStartXPosition + (tupletBracketEndXPosition - tupletBracketStartXPosition) / 2 - 6, tupletBracketStartYPosition + (tupletBracketEndYPosition - tupletBracketStartYPosition) / 2 + numberOfNotesYTranslation), element); }
/// <summary> /// Draws tuplet mark /// </summary> /// <param name="measurementService"></param> /// <param name="scoreService"></param> /// <param name="renderer"></param> /// <param name="element"></param> public static void TupletMark(IMeasurementService measurementService, IScoreService scoreService, ScoreRendererBase renderer, NoteOrRest element) { if (measurementService.TupletState == null) { throw new Exception("DrawTupletMark was called but no tuplet is currently open in staff."); } var tupletBracketPen = renderer.CreatePenFromDefaults(element, "tupletBracketThickness", s => s.DefaultTupletBracketThickness); Staff staff = scoreService.CurrentStaff; NoteOrRest firstElementInTuplet = staff.Peek <NoteOrRest>(element, PeekType.BeginningOfTuplet); int index = staff.Elements.IndexOf(firstElementInTuplet); List <MusicalSymbol> elementsUnderTuplet = staff.Elements.GetRange(index, staff.Elements.IndexOf(element) - index + 1); var noteGroupBounds = elementsUnderTuplet.OfType <Note>().GetBounds(renderer); if (IsDebugMode) { DrawNoteGroupOutline(renderer, noteGroupBounds, element); } var boundsOnOneSide = measurementService.TupletState.TupletPlacement == VerticalPlacement.Above ? new Tuple <Point, Point>(noteGroupBounds.NW, noteGroupBounds.NE) : new Tuple <Point, Point>(noteGroupBounds.SW, noteGroupBounds.SE); int placementMod = measurementService.TupletState.TupletPlacement == VerticalPlacement.Above ? -1 : 1; var bracketDefinition = new TupletBracketDefinition( boundsOnOneSide.Item1.X, boundsOnOneSide.Item1.Y + renderer.LinespacesToPixels(2) * placementMod, boundsOnOneSide.Item2.X, boundsOnOneSide.Item2.Y + renderer.LinespacesToPixels(2) * placementMod); if (measurementService.TupletState.AreSingleBeamsPresentUnderTuplet) //Draw tuplet bracket { renderer.DrawLine(bracketDefinition.StartPoint, bracketDefinition.Point25, tupletBracketPen, element); renderer.DrawLine(bracketDefinition.Point75, bracketDefinition.EndPoint, tupletBracketPen, element); renderer.DrawLine(bracketDefinition.StartPoint, bracketDefinition.StartPoint.Translate(0, renderer.LinespacesToPixels(-1) * placementMod), tupletBracketPen, element); renderer.DrawLine(bracketDefinition.EndPoint, bracketDefinition.EndPoint.Translate(0, renderer.LinespacesToPixels(-1) * placementMod), tupletBracketPen, element); } var tupletNumber = CalculateTupletNumber(elementsUnderTuplet.OfType <NoteOrRest>()); var textToWrite = renderer.Settings.MusicFontProfile.MusicFont.BuildTupletNumber(tupletNumber); var fontStyle = renderer.IsSMuFLFont ? MusicFontStyles.MusicFont : MusicFontStyles.LyricsFont; var textSize = renderer.CanMeasureString ? renderer.MeasureString(fontStyle, textToWrite) : new Size(); var textPosition = renderer.CanMeasureString ? bracketDefinition.MidPoint.Translate(textSize.Width / -2, textSize.Height / 2) : bracketDefinition.MidPoint.Translate(-3.7, 4.7); renderer.DrawString(textToWrite, fontStyle, textPosition, element); }
private NoteOrRest GetCurrentNoteOrRestAndDetermineTupletState(Staff staff) { NoteOrRest noteOrRest = CurrentElement as NoteOrRest; if (noteOrRest != null && noteOrRest.Voice < 2) { if (noteOrRest.Tuplet == TupletType.Start) { NoteOrRest tupletStart = staff.Peek <NoteOrRest>(noteOrRest, PeekType.BeginningOfTuplet); NoteOrRest tupletEnd = staff.Peek <NoteOrRest>(noteOrRest, PeekType.EndOfTuplet); if (tupletStart != null && tupletEnd != null) { TupletState = new Tuplet(); TupletState.NumberOfNotesUnderTuplet = staff.Elements.GetRange(staff.Elements.IndexOf(tupletStart), staff.Elements.IndexOf(tupletEnd) - staff.Elements.IndexOf(tupletStart)).OfType <NoteOrRest>().Where(nr => !(nr is Note) || (nr is Note && !((Note)nr).IsUpperMemberOfChord)).Count() + 1; } } } return(noteOrRest); }
internal static string GetDurationName(NoteOrRest symbol) { switch (symbol.BaseDuration.Denominator) { case 1: return("whole"); case 2: return("half"); case 4: return("quarter"); case 8: return("eighth"); case 16: return("16th"); case 32: return("32nd"); case 64: return("64th"); case 128: return("128th"); case 256: return("256th"); case 512: return("512th"); default: throw new ScoreException(symbol, $"Invalid rhythmic duration denominator {symbol.BaseDuration.Denominator}."); } }
/// <summary> /// Adds the note to the staff in the correct location /// </summary> /// <param name="nr">The note to add</param> private void addNoteOrRestToStaff(NoteOrRest nr) { // New for LStaff var elem = Viewer.SelectedElement; LStaff staff = getLStaff(elem); NoteOrRest noteToPlay; if (isValidTarget(elem)) { LMeasure measure = staff.getMeasure(elem); staff.AddAfter(elem, nr); if (measure.Contains(nr)) { Viewer.SelectedElement = nr; } else if (measure.Node.Next != null && measure.Node.Next.Value.Count > 0) { Viewer.SelectedElement = measure.Node.Next.Value.First.Value; } noteToPlay = (NoteOrRest)Viewer.SelectedElement; } else { staff.Add(nr); noteToPlay = (NoteOrRest)staff.Last.Value.Last(e => e.GetType().IsSubclassOf(typeof(NoteOrRest))); } // Play the note if (noteToPlay.GetType() == typeof(Note)) { model.PlayNote((Note)noteToPlay); } // Update the view model to make the Play command available model.updateView(); }
private void FetchNextElement(object state) { try { if (ShouldRestart) { CurrentElement = null; Enumerator = null; ShouldRestart = false; } TimerState timerState = state as TimerState; if (timerState == null) { return; } if (timerState.CancellationToken) { return; } if (timerState.Score == null) { return; } Staff staff = timerState.Score.Staves.FirstOrDefault(); if (staff == null) { return; } if (Enumerator == null) { Enumerator = staff.Elements.GetEnumerator(); } if (!Enumerator.MoveNext()) { Stop(); return; } CurrentElement = Enumerator.Current; NoteOrRest noteOrRest = GetCurrentNoteOrRestAndDetermineTupletState(staff); if (ProcessGraceNotesAndChordElements(staff)) { FetchNextElement(timerState); return; } // // Determine duration == next note start time // IHasDuration durationElement = CurrentElement as IHasDuration; if (durationElement != null) { double dueTime = MusicalSymbol.DurationToTime(durationElement, Tempo).TotalMilliseconds; if (TupletState != null) { dueTime = dueTime / TupletState.NumberOfNotesUnderTuplet * ((double)durationElement.BaseDuration.Denominator / (double)Tempo.BeatUnit.Denominator); } Debug.WriteLine("{0} with {1} dots will be played in {2} ms", durationElement.BaseDuration, durationElement.NumberOfDots, dueTime); Timer.Change((int)dueTime, Timeout.Infinite); } else { FetchNextElement(timerState); //If element does not have a duration, play next immediately } if (noteOrRest != null && noteOrRest.Tuplet == TupletType.Stop) { TupletState = null; } PlayElement(CurrentElement); } catch (Exception ex) { PlaybackExceptions.Add(ex); Stop(); } }
/// <summary> /// Adds a barline to the current measure if needed, and breaks the last note of the measure if it /// exceeds the allowed beats/measure, inserting the remainder into the next measure. Then recursively calls /// itself on subsequent measures until it hits a measure that does not overflow. /// </summary> /// <param name="m"></param> /// <param name="ts"></param> internal void fitMeasure(Measure m, TimeSignature ts) // Still need to add support for deletion { bool nextMeasureChanged = false; Staff staff = m.Staff; int measureIndex = staff.Measures.IndexOf(m); foreach (MusicalSymbol item in m.Elements) { // If new time signature, update if (item.GetType() == typeof(TimeSignature)) { ts = (TimeSignature)item; } // Make sure that any cleffs and signatures are at the beginning of the measure, before notes and rests if (item.GetType() == typeof(TimeSignature) || item.GetType() == typeof(Key) || item.GetType() == typeof(Clef)) { NoteOrRest firstBeat = getFirstBeat(m); if (m.Elements.IndexOf(firstBeat) < m.Elements.IndexOf(item)) { swapPositions(staff, item, firstBeat); } } } // While the current measure is not full and is not the end of the song, steal the first beat from the next measure // ****VERIFY THIS CODE ONCE NOTE DELETION IS IN PLACE**** while (getDurations(m).Sum() < ts.WholeNoteCapacity && m.Number < staff.Measures.Count && getFirstBeat(getNextMeasure(m)) != null) { Measure next = getNextMeasure(m); NoteOrRest nr = getFirstBeat(next); next.Elements.Remove(nr); m.Elements.Add(nr); // This note might be longer than we have room for, but the next loop will take care of it nextMeasureChanged = true; } // While the current measure has too many beats, push the extra into the next measure while (getDurations(m).Sum() > ts.WholeNoteCapacity) { double overage = getOverage(m, ts); // How much the content of the current measure exceeds its alloted time NoteOrRest lastNoteOrRest = (NoteOrRest)m.Elements.Last(e => e.GetType().IsSubclassOf(typeof(NoteOrRest))); // Last note or rest in measure double lastDurationValue = lastNoteOrRest.Duration.ToDouble(); // Value of last note or rest in measure int lastItemIndex = staff.Elements.IndexOf(lastNoteOrRest); // Index of last note or rest in the measure NoteOrRest itemToMove = null; // The last note or portion thereof that doesn't fit in the current measure // If the overage is because the last note is too big, break it into two notes if (lastDurationValue > overage) { NoteOrRest itemInPlace; // Shortened version of lastNoteOrRest that fits in currrent measure if (lastNoteOrRest.GetType() == typeof(Note)) { itemInPlace = new Note(((Note)lastNoteOrRest).Pitch, toRhythmicDuration(lastDurationValue - overage)); itemToMove = new Note(((Note)lastNoteOrRest).Pitch, toRhythmicDuration(overage)); // Remaining note value } else { itemInPlace = new Rest(toRhythmicDuration(lastDurationValue - overage)); itemToMove = new Rest(toRhythmicDuration(overage)); } // Have to remove lastNoteOrRest from both the staff and the measure staff.Elements.Remove(lastNoteOrRest); m.Elements.Remove(lastNoteOrRest); // Replace with shortened version, automatically populates to measure when adding staff.Elements.Insert(lastItemIndex, itemInPlace); } else { itemToMove = lastNoteOrRest; staff.Elements.Remove(lastNoteOrRest); m.Elements.Remove(lastNoteOrRest); lastItemIndex--; } // Refresh the updated measure in the staff.measures collection Barline bar = moveOrAddBarlineAfter(staff.Elements[lastItemIndex]); // Add itemToMove after the barline staff.Elements.Insert(lastItemIndex + 2, itemToMove); // Add it to the measure as well if it's not already there Measure nextMeasure = getNextMeasure(m); if (!nextMeasure.Elements.Contains(itemToMove)) { nextMeasure.Elements.Insert(0, itemToMove); } // set the flag to check the next measure nextMeasureChanged = true; } // If there are changes to the next measure, fix it. if (nextMeasureChanged) { fitMeasure(getNextMeasure(m), ts); } }
////// IN PROGRESS //////// /// <summary> /// Adds a barline to the current measure if needed, and breaks the last note of the measure if it /// exceeds the allowed beats/measure, inserting the remainder into the next measure, then recursively calls /// itself on subsequent measures until it hits a measure that does not overflow. /// </summary> /// <param name="m"></param> /// <param name="ts"></param> internal void fitMeasure(Measure m, TimeSignature ts) // Still need to add support for deletion { bool nextMeasureChanged = false; foreach (MusicalSymbol item in m.Elements) { // If new time signature, update if (item.GetType() == typeof(TimeSignature)) { ts = (TimeSignature)item; } // Make sure that any cleffs and signatures are at the beginning of the measure, before notes and rests if (item.GetType() == typeof(TimeSignature) || item.GetType() == typeof(Key) || item.GetType() == typeof(Clef)) { NoteOrRest firstBeat = getFirstBeat(m); if (m.Elements.IndexOf(firstBeat) < m.Elements.IndexOf(item)) { swapPositions(m.Staff, item, firstBeat); } } } // While the current measure is not full and is not the end of the song, steal the first beat from the next measure while (getDurations(m).Sum() < ts.WholeNoteCapacity && m.Number < m.Staff.Measures.Count && getFirstBeat(getNextMeasure(m)) != null) { Measure next = getNextMeasure(m); NoteOrRest nr = getFirstBeat(next); next.Elements.Remove(nr); m.Elements.Add(nr); // This note might be longer than we have room for, but the next loop will take care of it nextMeasureChanged = true; } Staff staff = m.Staff; int measureIndex = staff.Measures.IndexOf(m); // While the current measure has too many beats, push the extra into the next measure while (getDurations(m).Sum() > ts.WholeNoteCapacity) { //double[] d = getDurations(m); double overage = getOverage(m, ts);// d.Sum() - ts.WholeNoteCapacity; NoteOrRest lastNoteOrRest = (NoteOrRest)m.Elements.Last(e => e.GetType().IsSubclassOf(typeof(NoteOrRest))); double lastDurationValue = lastNoteOrRest.Duration.ToDouble(); int lastItemIndex = staff.Elements.IndexOf(lastNoteOrRest); NoteOrRest itemToMove = null; // If the overage is because the last note is too big, break it into two notes if (lastDurationValue > overage) { // Shorten the duration of the culprit to fit the measure //lastNoteOrRest.Duration = toRhythmicDuration(lastDurationValue - overage); // Instead of shortening in place, try replacing so the viewer notices the change NoteOrRest itemInPlace; // Propagate the change to the staff -- Never mind. It propagates fine here. //m.Staff.Elements[m.Staff.Elements.IndexOf(lastNoteOrRest)] = null; //m.Staff.Elements[m.Staff.Elements.IndexOf(lastNoteOrRest)] = lastNoteOrRest; // Add a copy of the culprit note or rest at the remaining duration if (lastNoteOrRest.GetType() == typeof(Note)) { itemInPlace = new Note(((Note)lastNoteOrRest).Pitch, toRhythmicDuration(lastDurationValue - overage)); itemToMove = new Note(((Note)lastNoteOrRest).Pitch, toRhythmicDuration(overage)); } else { itemInPlace = new Rest(toRhythmicDuration(lastDurationValue - overage)); itemToMove = new Rest(toRhythmicDuration(overage)); } m.Staff.Elements.Remove(lastNoteOrRest); m.Elements.Remove(lastNoteOrRest); m.Staff.Elements.Insert(lastItemIndex, itemInPlace); } else { itemToMove = lastNoteOrRest; m.Staff.Elements.Remove(lastNoteOrRest); m.Elements.Remove(lastNoteOrRest); lastItemIndex--; } // Grab the next barline and put it after the last note of the adjusted measure //Barline bar = moveOrAddBarlineAfter(staff.Elements[lastItemIndex -1 ]); // This is adding it to the staff elements, which have not been refreshed // Refresh the updated measure in the staff.measures collection if (m.Elements[m.Elements.Count - 1].GetType() != typeof(Barline)) { m.Staff.Elements.Insert(lastItemIndex + 1, new Barline()); // may have to use "add" } //*** May need to remove m from staff.measures and insert updated version*** // Add the new note/rest or new partial note/rest to the new measure // getNextMeasure(m).Elements.Insert(0, itemToMove); m.Staff.Elements.Insert(lastItemIndex + 2, itemToMove); // m.Staff.Elements.Insert(lastItemIndex + 1, lastElem); //staff.Elements.Insert(lastItemIndex + 2, itemToMove); updateView(); //getNextMeasure(m).Elements.Insert(0, itemToMove); nextMeasureChanged = true; // Refresh measures against the current staff //m = null; //m = staff.Measures.ElementAt(measureIndex); //nextMeasure = null; //nextMeasure = staff.Measures.ElementAt(measureIndex + 1); } // If there are changes to the next measure, fix it. if (nextMeasureChanged) //fitMeasure(staff.Measures[staff.Measures.IndexOf(m) + 1], ts); { fitMeasure(getNextMeasure(m), ts); } }
private static void DrawNoteGroupOutline(ScoreRendererBase renderer, Quadrangle noteGroupBounds, NoteOrRest element) { renderer.DrawLine(noteGroupBounds.NW, noteGroupBounds.NE, new Pen(Color.Red), element); renderer.DrawLine(noteGroupBounds.NE, noteGroupBounds.SE, new Pen(Color.Red), element); renderer.DrawLine(noteGroupBounds.SE, noteGroupBounds.SW, new Pen(Color.Red), element); renderer.DrawLine(noteGroupBounds.SW, noteGroupBounds.NW, new Pen(Color.Red), element); }