예제 #1
0
        /// <summary>
        /// Perfomrs Chomsky analysis and reduces list elements, extracting equal parts into newly created elements.
        /// The method is similar to compression.
        /// </summary>
        /// <param name="melodyPartNodes">All nodes including input and all produced nodes</param>
        /// <param name="scales">
        /// Scales for diatonic option. Within the list no scale should be similar to any other.
        /// Two scales are similar if one is inversion of another one,
        /// for example major and minor scale,
        /// but minor haromnic, minor melodic and minor natural are not similar to each other.
        /// If null, no diatonic option.
        /// </param>
        public static List <MelodyPartList> Reduce(IEnumerable <MelodyPartList> melodyPartNodes, List <TwelveToneSet> scales = null)
        {
            List <MelodyPartList> allNodes = new List <MelodyPartList>(melodyPartNodes);

            int maxListCount = int.MaxValue;

            List <TwelveToneSet> allScales = CalculateAllScales(scales);

            Swap swap  = FindLongestSwap(melodyPartNodes, allScales);
            int  count = 500;

            while (swap.list.Count > 2)
            {
                swap.AssertCorrect();

                // Assert the chunck size is not increasing
                Debug.Assert(swap.list.Count <= maxListCount);
                maxListCount = swap.list.Count;

                // If there is identical list already, don't add it, use the identical one
                MelodyPartList nodeExisting;
                if ((nodeExisting = allNodes.Find(node => node.IsIdentical(swap.list))) != null)
                {
                    swap.list = nodeExisting;
                }
                else
                {
                    allNodes.Add(swap.list);
                }

                //Assert that at most one part is whole
                if (swap.IsWhole1 && swap.IsWhole2 &&
                    swap.pnt1.part.type != MelodyPartList.Type.Voice &&
                    swap.pnt2.part.type != MelodyPartList.Type.Voice)
                {
                    Debug.Fail(message: "There are two identical parts. One should have referenced another instead of being created.");
                }

                int offset1 = swap.pnt1.MelodyPart.GetNotes().First().note;
                swap.pnt1.part.RemoveRange(swap.pnt1.firstNoteIndex, swap.list.Count);
                MelodyPart mp1 = swap.IsDToC ?
                                 new MelodyPartDtoC(offset1, swap.scales1.First(), swap.list) :
                                 new MelodyPart(offset1, swap.list);
                swap.pnt1.part.Insert(swap.pnt1.firstNoteIndex, mp1);

                if (!swap.IsWhole2 || swap.IsDToC)
                {
                    // If it's the same part, adjust the pnt2.index
                    if (swap.pnt1.part == swap.pnt2.part)
                    {
                        swap.pnt2.firstNoteIndex -= swap.list.Count - 1;
                    }

                    int offset2 = swap.pnt2.MelodyPart.GetNotes().First().note;
                    swap.pnt2.part.RemoveRange(swap.pnt2.firstNoteIndex, swap.list.Count);
                    MelodyPart mp2 = swap.IsDToC ?
                                     new MelodyPartDtoC(offset2, swap.scales2.First(), swap.list) :
                                     new MelodyPart(offset2, swap.list);

                    swap.pnt2.part.Insert(swap.pnt2.firstNoteIndex, mp2);
                }
                else
                {
                    // This is unexpected, but may be possible.
                    // If it happens rethink it.
                    Debug.Assert(!swap.IsDToC);
                }

                if (--count <= 0)
                {
                    break;
                }

                // Next swap
                swap = FindLongestSwap(allNodes, allScales);
            }
            ;

            foreach (var list in melodyPartNodes)
            {
                Debug.Assert(list.RecursiveCount > 1);
            }

            return(allNodes);
        }