/// <remarks><paramref name="x"/> must be an absolute position</remarks> public async Task DrawConnectionInstruction(VoicePart voicePart, double x, string instruction) { var y = this.Owner.GetHeight(voicePart, x); var bounds = await this.PrimitiveRenderer.DrawConnectionInstruction(x, y, instruction, voicePart.ToDirection()); this.EnsureHeightForOrnament(voicePart, bounds); }
public bool ToDocumentElement(TablatureContext context, ILogger logger, VoicePart voicePart, out BeatNote element) { var documentState = context.DocumentState; if (this.Fret != null && this.Fret.Value + documentState.MinimumCapoFret < (documentState.CapoFretOffsets?[this.String.Value - 1] ?? 0)) { logger.Report(LogLevel.Warning, this.Fret.Range, Messages.Warning_FretUnderCapo, this.String.Value, this.Fret.Value); } element = new BeatNote { Range = this.Range, PreConnection = this.PreConnection?.Value ?? PreNoteConnection.None, PostConnection = this.PostConnection?.Value ?? PostNoteConnection.None, IsTied = this.Tie != null, TiePosition = this.TiePosition?.Value, String = this.String.Value - 1, Fret = this.Fret?.Value ?? BeatNote.UnspecifiedFret, EffectTechnique = this.EffectTechnique?.Value ?? NoteEffectTechnique.None, EffectTechniqueParameter = this.EffectTechniqueParameter?.Value, Accent = this.Accent?.Value ?? NoteAccent.Normal }; if (!this.Validate(context, logger, voicePart, element)) { return(false); } return(true); }
private async Task DrawSemiBeam(BaseNoteValue noteValue, VoicePart voicePart, BeamSlope beamSlope, bool isLastOfBeam) { BeatRenderer beatRenderer1, beatRenderer2; if (isLastOfBeam) { beatRenderer1 = this.Root.GetRenderer <Beat, BeatRenderer>(this.Element.PreviousBeat); beatRenderer2 = this; } else { beatRenderer1 = this; beatRenderer2 = this.Root.GetRenderer <Beat, BeatRenderer>(this.Element.NextBeat); } var position1 = beatRenderer1.GetStemPosition(); var position2 = beatRenderer2.GetStemPosition(); var beamWidth = Math.Min(this.RenderingContext.Style.MaximumSemiBeamWidth, (position2 - position1) / 2); double x0, x1; if (isLastOfBeam) { x0 = position2 - beamWidth; x1 = position2; } else { x0 = position1; x1 = position1 + beamWidth; } await this.RenderingContext.DrawBeam(noteValue, x0, beamSlope.GetY(x0), x1, beamSlope.GetY(x1), voicePart); }
public async Task DrawBeatModifier(BeatModifier modifier, VoicePart voicePart, double x) { x += this.Location.X; var y = this.Owner.GetHeight(voicePart, x); var bounds = await this.PrimitiveRenderer.DrawBeatModifier(x, y, modifier, voicePart.ToDirection()); this.EnsureHeightForOrnament(voicePart, bounds); }
public async Task DrawArpeggioDown(VoicePart voicePart, double x) { x += this.Location.X; var y = this.Owner.GetHeight(voicePart, x); var bounds = await this.PrimitiveRenderer.DrawArpeggioDown(x, y, voicePart.ToDirection()); this.EnsureHeightForOrnament(voicePart, bounds); }
public async Task DrawTuplet(int tuplet, double x, VoicePart voicePart) { var y = this.Owner.GetHeight(voicePart, x + this.Location.X); var bounds = await this.PrimitiveRenderer.DrawTuplet(tuplet, x + this.Location.X, y); this.Owner.EnsureHeight(voicePart, bounds); }
public async Task DrawArtificialHarmonicText(VoicePart voicePart, double x, string text) { x += this.Location.X; var y = this.Owner.GetHeight(voicePart, x); var bounds = await this.PrimitiveRenderer.DrawArtificialHarmonicText(x, y, text, voicePart.ToDirection()); this.EnsureHeightForOrnament(voicePart, bounds); }
public bool ToDocumentElement(TablatureContext context, ILogger logger, VoicePart voicePart, out RhythmSegmentVoice voice) { voice = new RhythmSegmentVoice(voicePart) { Range = this.Range }; context.CurrentVoice = voice; foreach (var beat in this.Beats) { Beat documentBeat; if (!beat.ToDocumentElement(context, logger, voice, out documentBeat)) { return(false); } voice.Beats.Add(documentBeat); } // try to fill voice with rests if insufficient notes fed var duration = this.GetDuration(); if (duration < this.ExpectedDuration) { BaseNoteValue[] factors; if (!BaseNoteValues.TryFactorize(this.ExpectedDuration - duration, out factors)) { logger.Report(LogLevel.Error, this.Range, Messages.Error_InconsistentVoiceDurationCannotBeFilledWithRest); return(false); } logger.Report(LogLevel.Suggestion, this.Range, Messages.Suggestion_InconsistentVoiceDuration); foreach (var factor in factors) { var beat = new Beat() { NoteValue = new NoteValue(factor), IsRest = true, Notes = new BeatNote[0] }; context.CurrentVoice.IsTerminatedWithRest = true; voice.Beats.Add(beat); } } return(true); }
private void ConnectBars(Bar previousBar, Bar bar, VoicePart voicePart) { var firstBeat = bar.GetVoice(voicePart)?.GetFirstBeat(); var lastBeat = previousBar.GetVoice(voicePart)?.GetLastBeat(); if (firstBeat == null || lastBeat == null) { return; } firstBeat.PreviousBeat = lastBeat; lastBeat.NextBeat = firstBeat; }
public double GetHeight(VoicePart voicePart, double x) { switch (voicePart) { case VoicePart.Treble: return(this.GetBodyCeiling() - _heightMaps[voicePart].GetHeight(this.GetRelativeX(x))); case VoicePart.Bass: return(this.GetBodyFloor() + _heightMaps[voicePart].GetHeight(this.GetRelativeX(x))); default: throw new ArgumentOutOfRangeException(nameof(voicePart), voicePart, null); } }
public static VerticalDirection ToDirection(this VoicePart voicePart) { switch (voicePart) { case VoicePart.Treble: return(VerticalDirection.Above); case VoicePart.Bass: return(VerticalDirection.Under); default: throw new ArgumentOutOfRangeException(nameof(voicePart), voicePart, null); } }
private double GetBeamOffset(BaseNoteValue noteValue, VoicePart voicePart) { if (noteValue > BaseNoteValue.Eighth) { throw new ArgumentException("notes with a base note value longer than eighth can't be beamed", nameof(noteValue)); } var offset = (BaseNoteValue.Eighth - noteValue) * (this.Style.BeamThickness + this.Style.BeamSpacing) + 0.5 * this.Style.BeamThickness; return(voicePart == VoicePart.Treble ? offset : -offset); }
public Voice GetVoice(VoicePart part) { switch (part) { case VoicePart.Treble: return(this.TrebleVoice); case VoicePart.Bass: return(this.BassVoice); default: throw new ArgumentOutOfRangeException(nameof(part), part, null); } }
public void DrawStem(VoicePart voicePart, double x, double y0, double y1) { x = this.Location.X + x; var yFrom = this.Location.Y + Math.Min(y0, y1); var yTo = this.Location.Y + Math.Max(y0, y1); this.PrimitiveRenderer.DrawStem(x, yFrom, yTo); this.Owner.EnsureHeight(voicePart, x - this.Style.NoteStemHorizontalMargin, x + this.Style.NoteStemHorizontalMargin, this.Owner.Location.Y + y0, this.Owner.Location.Y + y1, this.Style.NoteTailVerticalMargin); }
public async Task DrawFlag(BaseNoteValue noteValue, double x, double y, VoicePart voicePart) { if (noteValue > BaseNoteValue.Eighth) { return; } var bounds = await this.PrimitiveRenderer.DrawFlag(noteValue, this.Location.X + x, this.Location.Y + y, voicePart.ToDirection()); this.Owner.EnsureHeight(voicePart, bounds.Left - this.Style.NoteStemHorizontalMargin, bounds.Right, bounds.Top, bounds.Bottom); }
private bool Validate(TablatureContext context, ILogger logger, VoicePart voicePart, BeatNote element) { if (!this.ValidateTie(context, logger, element)) { return(false); } if (!this.ValidatePreConnection(context, logger, voicePart, element)) { return(false); } if (!this.ValidatePostConnection(context, logger, voicePart, element)) { return(false); } return(true); }
/// <summary> /// Ensure height spanned between <paramref name="x0" /> and <paramref name="x1" /> in the /// height map by selecting between <paramref name="y0" /> and <paramref name="y1" /> according to /// the specified <paramref name="voicePart"/> /// </summary> /// <remarks>All coordinates are absolute</remarks> public void EnsureHeight(VoicePart voicePart, double x0, double x1, double y0, double y1, double vMargin = 0) { double height; switch (voicePart) { case VoicePart.Treble: height = this.GetBodyCeiling() - Math.Min(y0, y1); break; case VoicePart.Bass: height = Math.Max(y0, y1) - this.GetBodyFloor(); break; default: throw new ArgumentOutOfRangeException(nameof(voicePart), voicePart, null); } _heightMaps[voicePart].EnsureHeight(this.GetRelativeX(x0), x1 - x0, height + vMargin); }
/// <summary> /// Ensure height spanned between <paramref name="x0" /> and <paramref name="x1" /> in the /// height map by lerping between <paramref name="y0" /> and <paramref name="y1" />. /// </summary> /// <remarks>All coordinates are absolute</remarks> public void EnsureHeightSloped(VoicePart voicePart, double x0, double x1, double y0, double y1, double vMargin, double hMargin) { switch (voicePart) { case VoicePart.Treble: y0 = this.GetBodyCeiling() - y0; y1 = this.GetBodyCeiling() - y1; break; case VoicePart.Bass: y0 = y0 - this.GetBodyFloor(); y1 = y1 - this.GetBodyFloor(); break; default: throw new ArgumentOutOfRangeException(nameof(voicePart), voicePart, null); } _heightMaps[voicePart].EnsureHeight(this.GetRelativeX(x0), x1 - x0, y0 + vMargin, y1 + vMargin, hMargin); }
public void GetStemOffsetRange(int columnIndex, int stringIndex, VoicePart voicePart, out double from, out double to) { var noteBounds = this.GetNoteBoundingBox(columnIndex, stringIndex); if (voicePart == VoicePart.Treble) { var baseFrom = noteBounds?.Top ?? this.Owner.GetStringSpacePosition(stringIndex); from = baseFrom - this.Style.NoteStemOffset; to = Math.Min(from - this.Style.NoteStemHeight, this.Owner.GetBodyCeiling() - this.Style.MinimumNoteTailOffset); } else { var baseFrom = noteBounds?.Bottom ?? this.Owner.GetStringSpacePosition(stringIndex + 1); from = baseFrom + this.Style.NoteStemOffset; to = Math.Max(from + this.Style.NoteStemHeight, this.Owner.GetBodyFloor() + this.Style.MinimumNoteTailOffset); } from -= this.Location.Y; to -= this.Location.Y; }
public RhythmSegmentVoice(VoicePart part) { this.Part = part; this.Beats = new List <Beat>(); }
public void EnsureHeight(VoicePart voicePart, Rect bounds) { this.EnsureHeight(voicePart, bounds.Left, bounds.Right, bounds.Top, bounds.Bottom); }
public HeightMap GetHeightMap(VoicePart voicePart) { return(_heightMaps[voicePart]); }
private void EnsureHeightForOrnament(VoicePart voicePart, Rect bounds) { bounds.Inflate(this.Style.BeatOrnamentMargin, this.Style.BeatOrnamentMargin); this.Owner.EnsureHeight(voicePart, bounds); }
private bool ValidatePreConnection(TablatureContext context, ILogger logger, VoicePart voicePart, BeatNote element) { if (this.PreConnection == null || this.PreConnection.Value == PreNoteConnection.None) { return(true); } if (this.PreConnection.Value == PreNoteConnection.SlideInFromHigher || this.PreConnection.Value == PreNoteConnection.SlideInFromLower) { if (this.Fret == null) { logger.Report(LogLevel.Error, this.PreConnection.Range, Messages.Error_FretMissingForSlideInNote); return(false); } if (this.PreConnection.Value == PreNoteConnection.SlideInFromLower && this.Fret.Value <= context.DocumentState.CapoFretOffsets[this.String.Value - 1]) { logger.Report(LogLevel.Warning, this.Fret.Range, Messages.Warning_FretTooLowForSlideInNote); element.PreConnection = PreNoteConnection.None; } return(true); } if (!this.RetrievePreConnectedNote(context, logger, element)) { return(false); } switch (this.PreConnection.Value) { case PreNoteConnection.Slide: if (this.Fret == null) { logger.Report(LogLevel.Error, this.PreConnection.Range, Messages.Error_FretMissingForSlideNote); return(false); } if (this.Fret.Value == element.PreConnectedNote.Fret) { logger.Report(LogLevel.Warning, this.PreConnection.Range, Messages.Warning_SlidingToSameFret); element.PreConnection = PreNoteConnection.None; } return(true); case PreNoteConnection.Hammer: if (this.Fret == null) { logger.Report(LogLevel.Error, this.PreConnection.Range, Messages.Error_FretMissingForHammerNote); return(false); } if (this.Fret.Value == element.PreConnectedNote.Fret) { logger.Report(LogLevel.Warning, this.Fret.Range, Messages.Warning_HammeringToSameFret); element.PreConnection = PreNoteConnection.None; } else if (this.Fret.Value < element.PreConnectedNote.Fret) { logger.Report(LogLevel.Warning, this.Fret.Range, Messages.Warning_HammeringFromHigherFret); element.PreConnection = PreNoteConnection.Pull; } return(true); case PreNoteConnection.Pull: if (this.Fret == null) { logger.Report(LogLevel.Error, this.PreConnection.Range, Messages.Error_FretMissingForPullNote); return(false); } if (this.Fret.Value == element.PreConnectedNote.Fret) { logger.Report(LogLevel.Warning, this.Fret.Range, Messages.Warning_PullingToSameFret); element.PreConnection = PreNoteConnection.None; } else if (this.Fret.Value > element.PreConnectedNote.Fret) { logger.Report(LogLevel.Warning, this.Fret.Range, Messages.Warning_PullingFromLowerFret); element.PreConnection = PreNoteConnection.Hammer; } return(true); default: throw new ArgumentOutOfRangeException(); } }
// x is absolute position public Task <Rect> DrawGliss(double x, int stringIndex, GlissDirection direction, VoicePart voicePart) { return(this.PrimitiveRenderer.DrawGliss(x, this.Owner.GetStringPosition(stringIndex), direction)); }
public Voice(Bar ownerBar, VoicePart voicePart) { this.OwnerBar = ownerBar; this.VoicePart = voicePart; this.BeatElements = new List <IBeatElement>(); }
private bool ValidatePostConnection(TablatureContext context, ILogger logger, VoicePart voicePart, BeatNote element) { if (this.PostConnection == null || this.PostConnection.Value == PostNoteConnection.None) { return(true); } if (this.PostConnection.Value == PostNoteConnection.SlideOutToHigher || this.PostConnection.Value == PostNoteConnection.SlideOutToLower) { if (this.Fret == null) { logger.Report(LogLevel.Error, this.PreConnection.Range, Messages.Error_FretMissingForSlideOutNote); return(false); } if (this.PostConnection.Value == PostNoteConnection.SlideOutToLower && this.Fret.Value <= context.DocumentState.GetCapoFretOffset(this.String.Value - 1)) { logger.Report(LogLevel.Warning, this.Fret.Range, Messages.Warning_FretTooLowForSlideOutNote); element.PostConnection = PostNoteConnection.None; } } return(true); }
public async Task DrawBeam(BaseNoteValue noteValue, double x0, double y0, double x1, double y1, VoicePart voicePart) { var offset = this.GetBeamOffset(noteValue, voicePart); x0 = x0 + this.Location.X; y0 = y0 + this.Location.Y + offset; x1 = x1 + this.Location.X; y1 = y1 + this.Location.Y + offset; var bounds = await this.PrimitiveRenderer.DrawBeam(x0, y0, x1, y1); var beamHalfThickness = voicePart == VoicePart.Treble ? Math.Min(y0, y1) - bounds.Top : bounds.Bottom - Math.Max(y0, y1); this.Owner.EnsureHeightSloped(voicePart, x0, x1, y0, y1, this.Style.NoteTailVerticalMargin + beamHalfThickness, this.Style.NoteStemHorizontalMargin); }