示例#1
0
        /// <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);
        }
示例#2
0
        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);
        }
示例#3
0
        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);
        }
示例#4
0
        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);
        }
示例#5
0
        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);
        }
示例#6
0
        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);
        }
示例#7
0
        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);
        }
示例#8
0
        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);
        }
示例#9
0
        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;
        }
示例#10
0
        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);
            }
        }
示例#11
0
        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);
            }
        }
示例#12
0
        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);
        }
示例#13
0
        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);
            }
        }
示例#14
0
        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);
        }
示例#15
0
        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);
        }
示例#16
0
        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);
        }
示例#17
0
        /// <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);
        }
示例#18
0
        /// <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);
        }
示例#19
0
        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;
        }
示例#20
0
 public RhythmSegmentVoice(VoicePart part)
 {
     this.Part  = part;
     this.Beats = new List <Beat>();
 }
示例#21
0
 public void EnsureHeight(VoicePart voicePart, Rect bounds)
 {
     this.EnsureHeight(voicePart, bounds.Left, bounds.Right, bounds.Top, bounds.Bottom);
 }
示例#22
0
 public HeightMap GetHeightMap(VoicePart voicePart)
 {
     return(_heightMaps[voicePart]);
 }
示例#23
0
 private void EnsureHeightForOrnament(VoicePart voicePart, Rect bounds)
 {
     bounds.Inflate(this.Style.BeatOrnamentMargin, this.Style.BeatOrnamentMargin);
     this.Owner.EnsureHeight(voicePart, bounds);
 }
示例#24
0
        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();
            }
        }
示例#25
0
 // 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));
 }
示例#26
0
 public Voice(Bar ownerBar, VoicePart voicePart)
 {
     this.OwnerBar     = ownerBar;
     this.VoicePart    = voicePart;
     this.BeatElements = new List <IBeatElement>();
 }
示例#27
0
        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);
        }
示例#28
0
        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);
        }