예제 #1
0
        void AddChordDots(ABC.Chord chord, ABC.Clef clef, NoteDirection noteDirection, Bounds rootItem, GameObject container, ref Bounds totalBounds)
        {
            int oddStepOffset = noteDirection == NoteDirection.Up ? 1 : -1;
            var dotSet        = new HashSet <int>();

            foreach (var note in chord.notes)
            {
                int stepCount = note.pitch - clefZero[clef];

                if (stepCount % 2 == 1)
                {
                    stepCount += oddStepOffset;
                }

                dotSet.Add(stepCount);
            }

            foreach (var step in dotSet)
            {
                var positionX = rootItem.max.x;

                for (int i = 0; i < chord.dotCount; i++)
                {
                    var dot       = CreateNoteDot(step, container, positionX + dotAdvance);
                    var dotBounds = dot.bounds;
                    positionX += dotBounds.size.x;
                    totalBounds.Encapsulate(dotBounds);
                }
            }
        }
예제 #2
0
        private Bounds CreateWholeNoteChordNotes(ABC.Chord chord, ABC.Clef clef, GameObject container, Vector3 offset, ref Bounds totalBounds)
        {
            var lastNotePos = Vector3.zero;
            var rootBounds  = AddChordNoteHead(chord.notes[0].pitch, ABC.Length.Whole, clef, NoteDirection.Down, container, offset, ref lastNotePos);

            totalBounds.Encapsulate(rootBounds);
            bool right = false;

            for (int i = 1; i < chord.notes.Length; i++)
            {
                var    noteOffset = offset;
                Bounds itemBounds;
                if (!right && chord.notes[i].pitch - chord.notes[i - 1].pitch == 1)
                {
                    noteOffset.x += compressedChordWholeNoteOffset;
                    right         = true;
                }
                else
                {
                    right = false;
                }

                itemBounds = AddChordNoteHead(chord.notes[i].pitch, ABC.Length.Whole, clef, NoteDirection.Down, container, noteOffset, ref lastNotePos);
                totalBounds.Encapsulate(itemBounds);

                if (!right)
                {
                    rootBounds.Encapsulate(itemBounds);
                }
            }

            return(rootBounds);
        }
예제 #3
0
        private NoteInfo CreateChord(ABC.Chord chord, ABC.Clef clef, NoteDirection noteDirection, Beam beam, IReadOnlyList <string> decorations, GameObject container)
        {
            var offset      = Vector3.zero;
            var totalBounds = new Bounds();

            totalBounds.SetMinMax(offset, offset);

            if (CreateChordAccidentals(chord.notes, clef, ref offset, container, ref totalBounds))
            {
                offset.x = totalBounds.max.x + accidentalOffset;
            }

            var staffMarkers = CreateChordStaffMarkers(noteDirection, chord, clef, offset.x, compressedChordNoteOffset, ref totalBounds);

            if (staffMarkers != null) // this ensures that the note appears centered w.r.t the markers
            {
                offset += new Vector3(staffMarkerNoteOffset, 0.0f, 0.0f);
            }

            Bounds rootBounds = CreateChordNotes(noteDirection, chord, beam, clef, container, offset, ref totalBounds);

            AddDecorations(chord, decorations, rootBounds, container, ref totalBounds);

            if (chord.dotCount > 0)
            {
                AddChordDots(chord, clef, noteDirection, totalBounds, container, ref totalBounds);
            }

            if (staffMarkers != null)
            {
                staffMarkers.transform.parent = container.transform;
            }

            return(new NoteInfo(rootBounds, totalBounds));
        }
예제 #4
0
        private bool CreateChordAccidentals(ABC.Chord.Element[] notes, ABC.Clef clef, ref Vector3 offset, GameObject container, ref Bounds totalBounding)
        {
            var accidentalLevels = ComputeChordAccidentalLevels(notes, clefZero[clef]);

            if (accidentalLevels == null)
            {
                return(false);
            }

            for (int i = accidentalLevels.Count - 1; i >= 0; i--)
            {
                foreach (var note in accidentalLevels[i])
                {
                    int stepCount = note.pitch - clefZero[clef];

                    var accidental = spriteCache.GetSpriteObject($"Accidental_{note.accidental}");
                    accidental.transform.parent        = container.transform;
                    accidental.transform.localPosition = offset + new Vector3(0.0f, noteStep * stepCount, 0.0f);
                    totalBounding.Encapsulate(accidental.bounds);
                }

                offset += new Vector3(accidentalWidth, 0.0f, 0.0f);
            }

            return(true);
        }
예제 #5
0
        public NoteInfo CreateNote(ABC.Note note, ABC.Clef clef, IReadOnlyList <string> decorations, GameObject container)
        {
            int stepCount     = note.pitch - clefZero[clef];
            var noteDirection = stepCount > 3 ? NoteDirection.Down : NoteDirection.Up;

            return(CreateNote(note, clef, stepCount, null, noteDirection, decorations, container));
        }
예제 #6
0
        public void Init(ABC.Clef clef)
        {
            this.clef = clef;
            var info = notes.CreateStaff(this.clef, container, Vector3.zero);

            SetWidth(info.totalBounding.size.x);
            isInit = true;
        }
예제 #7
0
        public NoteInfo CreateChord(ABC.Chord chord, ABC.Clef clef, IReadOnlyList <string> decorations, GameObject container)
        {
            var noteDirection = DetermineChordNoteDirection(chord.notes, clef);

            if (chord.length == ABC.Length.Whole)
            {
                return(CreateWholeNoteChord(chord, clef, decorations, container));
            }
            else
            {
                return(CreateChord(chord, clef, noteDirection, null, decorations, container));
            }
        }
예제 #8
0
        private Bounds AddChordNoteHead(ABC.Pitch value, ABC.Length length, ABC.Clef clef, NoteDirection noteDirection, GameObject container, Vector3 offset, ref Vector3 notePos)
        {
            int stepCount = value - clefZero[clef];

            notePos = offset + new Vector3(0.0f, noteStep * stepCount, 0.0f);

            var spriteName = length == ABC.Length.Whole ? "Note_Whole" : $"Chord_{length}";
            var dot        = spriteCache.GetSpriteObject(spriteName);

            dot.transform.parent        = container.transform;
            dot.transform.localPosition = notePos;

            return(dot.bounds);
        }
예제 #9
0
        void LayoutStaff(VoiceLayout.ScoreLine scoreLine, ABC.Clef clef)
        {
            var staffSprite = cache.GetSpriteObject("Staff");

            staffSprite.transform.parent        = scoreLine.container.transform;
            staffSprite.transform.localPosition = scoreLine.insertPos;
            scoreLine.bounds = staffSprite.bounds;

            scoreLine.AdvaceInsertPos(staffPadding);
            var clefSprite = cache.GetSpriteObject($"Clef_{clef}");

            clefSprite.transform.parent        = scoreLine.container.transform;
            clefSprite.transform.localPosition = scoreLine.insertPos;
            scoreLine.bounds.Encapsulate(clefSprite.bounds);
            scoreLine.AdvaceInsertPos(staffPadding);
        }
예제 #10
0
        private GameObject CreateChordStaffMarkersDown(ABC.Chord chord, ABC.Clef clef, float position, float offsetSize, ref Bounds totalBounds)
        {
            int stepCount = chord.notes[chord.notes.Length - 1].pitch - clefZero[clef];

            if (stepCount < 9)
            {
                return(null);
            }

            if (stepCount % 2 == 0)
            {
                stepCount -= 1;
            }

            var   staffMarkers    = new GameObject("Staff Markers");
            float staffMarkerSize = chord.length < ABC.Length.Whole ? 1.0f : wholeNoteStaffMarkerSize;

            for (int step = stepCount; step >= 9; step -= 2)
            {
                totalBounds.Encapsulate(CreateStaffMark(step, staffMarkers, position, staffMarkerSize));
            }

            for (int i = chord.notes.Length - 2; i >= 0; i--)
            {
                if (stepCount < 9)
                {
                    break;
                }

                if (chord.notes[i + 1].pitch - chord.notes[i].pitch == 1)
                {
                    stepCount = chord.notes[i].pitch - clefZero[clef];
                    if (stepCount % 2 == 0)
                    {
                        stepCount -= 1;
                    }

                    for (int step = stepCount; step >= 9; step -= 2)
                    {
                        totalBounds.Encapsulate(CreateStaffMark(step, staffMarkers, position + offsetSize, staffMarkerSize));
                    }
                    break;
                }
            }

            return(staffMarkers);
        }
예제 #11
0
        private GameObject CreateChordStaffMarkersUp(ABC.Chord chord, ABC.Clef clef, float position, float offsetSize, ref Bounds totalBounds)
        {
            int stepCount = chord.notes[0].pitch - clefZero[clef];

            if (stepCount > -3)
            {
                return(null);
            }

            if (stepCount % 2 == 0)
            {
                stepCount += 1;
            }

            var   staffMarkers    = new GameObject("Staff Markers");
            float staffMarkerSize = chord.length < ABC.Length.Whole ? 1.0f : wholeNoteStaffMarkerSize;

            for (int step = stepCount; step <= -3; step += 2)
            {
                totalBounds.Encapsulate(CreateStaffMark(step, staffMarkers, position, staffMarkerSize));
            }

            for (int i = 1; i < chord.notes.Length; i++)
            {
                if (stepCount > -3)
                {
                    break;
                }

                if (chord.notes[i].pitch - chord.notes[i - 1].pitch == 1)
                {
                    stepCount = chord.notes[i].pitch - clefZero[clef];
                    if (stepCount % 2 == 0)
                    {
                        stepCount += 1;
                    }

                    for (int step = stepCount; step <= -3; step += 2)
                    {
                        totalBounds.Encapsulate(CreateStaffMark(step, staffMarkers, position + offsetSize, staffMarkerSize));
                    }
                    break;
                }
            }

            return(staffMarkers);
        }
예제 #12
0
        public NoteInfo CreateStaff(ABC.Clef clef, GameObject container, Vector3 offset)
        {
            var staff = spriteCache.GetSpriteObject("Staff");

            staff.transform.parent        = container.transform;
            staff.transform.localPosition = offset;

            Bounds bounds = staff.bounds;

            offset.x += Layout.staffPadding;

            var clefSprite = spriteCache.GetSpriteObject($"Clef_{clef}");

            clefSprite.transform.parent        = container.transform;
            clefSprite.transform.localPosition = offset;
            bounds.Encapsulate(clefSprite.bounds);

            return(new NoteInfo(bounds, bounds));
        }
예제 #13
0
        void CreateNoteSprite(ABC.Clef clef, VoiceLayout.ScoreLine.Element element)
        {
            var noteItem = element.item as ABC.Note;

            element.container = new GameObject("Note");

            tune.decorations.TryGetValue(noteItem.id, out var decorations);

            NoteInfo noteInfo;

            if (beams.TryGetValue(noteItem.beam, out Beam beam))
            {
                noteInfo = notes.CreateNote(noteItem, beam, decorations, element.container);
            }
            else
            {
                noteInfo = notes.CreateNote(noteItem, clef, decorations, element.container);
            }

            element.info = noteInfo;
        }
예제 #14
0
        void CreateChordSprite(ABC.Clef clef, VoiceLayout.ScoreLine.Element element)
        {
            var chordItem = element.item as ABC.Chord;

            element.container = new GameObject("Chord");

            tune.decorations.TryGetValue(chordItem.id, out var decorations);

            NoteInfo chordInfo;

            if (beams.TryGetValue(chordItem.beam, out Beam beam))
            {
                chordInfo = notes.CreateChord(chordItem, beam, decorations, element.container);
            }
            else
            {
                chordInfo = notes.CreateChord(chordItem, clef, decorations, element.container);
            }

            element.info = chordInfo;
        }
예제 #15
0
        private GameObject CreateNoteStaffMarkersDown(ABC.Note note, ABC.Clef clef, float position, ref Bounds totalBounds)
        {
            int stepCount = note.pitch - clefZero[clef];

            if (stepCount < 9)
            {
                return(null);
            }

            if (stepCount % 2 == 0)
            {
                stepCount -= 1;
            }

            var   staffMarkers    = new GameObject("Staff Markers");
            float staffMarkerSize = note.length < ABC.Length.Whole ? 1.0f : wholeNoteStaffMarkerSize;

            for (int step = stepCount; step >= 9; step -= 2)
            {
                totalBounds.Encapsulate(CreateStaffMark(step, staffMarkers, position, staffMarkerSize));
            }

            return(staffMarkers);
        }
예제 #16
0
 private Bounds CreateChordNotes(NoteDirection noteDirection, ABC.Chord chord, Beam beam, ABC.Clef clef, GameObject container, Vector3 offset, ref Bounds totalBounds)
 {
     if (noteDirection == NoteDirection.Up)
     {
         return(CreateChordNotesUp(chord, beam, clef, container, offset, ref totalBounds));
     }
     else
     {
         return(CreateChordNotesDown(chord, beam, clef, container, offset, ref totalBounds));
     }
 }
예제 #17
0
        /// <summary>
        /// The direction of the chord stem is chosen to minimize stem length outside the staff.
        /// The direction is chosen by examining the extreme notes of the chord and picking the direction based on the note which is farthest away from the value of the middle staffline.
        /// Precondition: notes array should be sorted in ascending order.
        /// </summary>
        private NoteDirection DetermineChordNoteDirection(ABC.Chord.Element[] sortedNotes, ABC.Clef clef)
        {
            var direction = NoteDirection.Down;

            var clefCenter   = clefZero[clef] + 3;
            int lowDistance  = Math.Abs(sortedNotes[0].pitch - clefCenter);
            int highDistance = Math.Abs(sortedNotes[sortedNotes.Length - 1].pitch - clefCenter);

            if (lowDistance > highDistance)
            {
                direction = NoteDirection.Up;
            }

            return(direction);
        }
예제 #18
0
 public Beam(int id, ABC.Clef clef)
 {
     this.id   = id;
     this.clef = clef;
 }
예제 #19
0
 private GameObject CreateChordStaffMarkers(NoteDirection noteDirection, ABC.Chord chord, ABC.Clef clef, float position, float offsetSize, ref Bounds totalBounds)
 {
     if (noteDirection == NoteDirection.Up)
     {
         return(CreateChordStaffMarkersUp(chord, clef, position, offsetSize, ref totalBounds));
     }
     else
     {
         if (ChordIsCompressed(chord, NoteDirection.Down))
         {
             position += offsetSize;
         }
         return(CreateChordStaffMarkersDown(chord, clef, position, -offsetSize, ref totalBounds));
     }
 }
예제 #20
0
 private GameObject CreateNoteStaffMarkers(NoteDirection noteDirection, ABC.Note note, ABC.Clef clef, float position, ref Bounds totalBounds)
 {
     if (noteDirection == NoteDirection.Up)
     {
         return(CreateNoteStaffMarkersUp(note, clef, position, ref totalBounds));
     }
     else
     {
         return(CreateNoteStaffMarkersDown(note, clef, position, ref totalBounds));
     }
 }
예제 #21
0
        private Bounds CreateChordNotesDown(ABC.Chord chord, Beam beam, ABC.Clef clef, GameObject container, Vector3 offset, ref Bounds totalBounds)
        {
            var stem = spriteCache.GetSpriteObject("Note_Stem_Down");

            stem.transform.parent = container.transform;
            var lastNotePos = Vector3.zero;

            if (ChordIsCompressed(chord, NoteDirection.Down))
            {
                offset.x += compressedChordNoteOffset;
            }

            var dotValue    = chord.length > ABC.Length.Quarter ? chord.length : ABC.Length.Quarter;
            var rootBounds  = AddChordNoteHead(chord.notes[chord.notes.Length - 1].pitch, dotValue, clef, NoteDirection.Down, container, offset, ref lastNotePos);
            var chordBounds = rootBounds;
            var stemPos     = container.transform.GetChild(container.transform.childCount - 1).localPosition + Beam.stemDownOffset;

            stem.transform.localPosition = stemPos;

            bool left = false;

            for (int i = chord.notes.Length - 2; i >= 0; i--)
            {
                var noteOffset = offset;
                if (!left && chord.notes[i + 1].pitch - chord.notes[i].pitch == 1)
                {
                    noteOffset.x -= compressedChordNoteOffset;
                    left          = true;
                }
                else
                {
                    left = false;
                }

                var noteBounds = AddChordNoteHead(chord.notes[i].pitch, dotValue, clef, NoteDirection.Down, container, noteOffset, ref lastNotePos);
                chordBounds.Encapsulate(noteBounds);
                if (!left)
                {
                    rootBounds.Encapsulate(noteBounds);
                }
            }

            SpriteRenderer flag       = null;
            float          stemHeight = Mathf.Abs((lastNotePos.y - Beam.defaultStemHeight) - stemPos.y);

            if (beam == null)
            {
                // If this chord is not part of a beam it may need to have a flag attached.
                if (chord.length <= ABC.Length.Eighth)
                {
                    flag = spriteCache.GetSpriteObject($"Note_Flag_{chord.length}_Down");
                    flag.transform.parent = container.transform;
                }
            }
            else if (beam.stemHeight != Beam.unspecifiedStemHeight)
            {
                // beam contains notes at different heights, used the calculated stem height
                stemHeight = Mathf.Abs(stemHeight - stemPos.y);
            }

            stem.transform.localScale = new Vector3(1.0f, stemHeight, 1.0f);

            var stemBounds = stem.bounds;

            if (flag != null)
            {
                flag.transform.localPosition = new Vector3(stemBounds.max.x, stemBounds.min.y, 0.0f);
            }

            rootBounds.Encapsulate(stemBounds);
            chordBounds.Encapsulate(stemBounds);
            totalBounds.Encapsulate(chordBounds);

            return(rootBounds);
        }
예제 #22
0
        private NoteInfo CreateNote(ABC.Note note, ABC.Clef clef, int noteStepCount, Beam beam, NoteDirection noteDirection, IReadOnlyList <string> decorations, GameObject container)
        {
            var notePosition = new Vector3(0.0f, noteStep * noteStepCount, 0.0f);

            var totalBounds = new Bounds();

            totalBounds.SetMinMax(Vector3.zero, Vector3.zero);
            if (note.accidental != ABC.Accidental.Unspecified)
            {
                var accidental = spriteCache.GetSpriteObject($"Accidental_{note.accidental}");
                accidental.transform.parent        = container.transform;
                accidental.transform.localPosition = notePosition;

                totalBounds.Encapsulate(accidental.bounds);
                notePosition.x = totalBounds.size.x + accidentalOffset;
            }

            GameObject staffMarkers = null;

            if (NeedsStaffMarkers(noteStepCount))
            {
                staffMarkers  = CreateNoteStaffMarkers(noteDirection, note, clef, notePosition.x, ref totalBounds);
                notePosition += new Vector3(staffMarkerNoteOffset, 0.0f, 0.0f);
            }

            Bounds         rootItemBounds;
            SpriteRenderer rootItem = null;

            if (beam != null && beam.stemHeight != Beam.unspecifiedStemHeight)
            {
                var noteHead = spriteCache.GetSpriteObject("Chord_Quarter");
                noteHead.transform.parent        = container.transform;
                noteHead.transform.localPosition = notePosition;
                rootItemBounds = noteHead.bounds;

                rootItem = spriteCache.GetSpriteObject($"Note_Stem_{noteDirection}");
                rootItem.transform.parent = container.transform;

                var stemPos = notePosition + (noteDirection == NoteDirection.Up ? Beam.stemUpOffset : Beam.stemDownOffset);
                rootItem.transform.localPosition = stemPos;
                rootItem.transform.localScale    = new Vector3(1.0f, Mathf.Abs(beam.stemHeight - stemPos.y), 1.0f);
                rootItemBounds.Encapsulate(rootItem.bounds);
            }
            else
            {
                var spriteName = GetNoteSpriteName(note.length, note.beam != 0, noteDirection);
                rootItem = spriteCache.GetSpriteObject(spriteName);
                rootItem.transform.parent        = container.transform;
                rootItem.transform.localPosition = notePosition;

                rootItemBounds = rootItem.bounds;
            }

            totalBounds.Encapsulate(rootItemBounds);

            for (int i = 0; i < note.dotCount; i++)
            {
                float dotOffset = rootItemBounds.max.x + dotAdvance;
                var   dot       = CreateNoteDot(noteStepCount, container, dotOffset);
                totalBounds.Encapsulate(dot.bounds);
            }

            AddDecorations(note, decorations, rootItemBounds, container, ref totalBounds);

            if (staffMarkers != null)
            {
                staffMarkers.transform.parent = container.transform;
            }

            return(new NoteInfo(rootItemBounds, totalBounds));
        }