public static void Normalize(List <Smi> smis)
        {
            int startIndex = -1;

            for (int i = 1; i < smis.Count; i++)
            {
                if (smis[i].syncType == SyncType.inner)
                {
                    if (startIndex < 0)
                    {
                        startIndex = i - 1;
                    }
                }
                else
                {
                    if (startIndex >= 0)
                    {
                        int endIndex = i;
                        if (smis[startIndex].syncType == smis[endIndex].syncType)
                        {
                            int startSync = smis[startIndex].start;
                            int endSync   = smis[endIndex].start;
                            int count     = endIndex - startIndex;

                            for (int j = 1; j < count; j++)
                            {
                                smis[startIndex + j].start = ((count - j) * startSync + j * endSync) / count;
                            }
                        }
                        startIndex = -1;
                    }
                }
            }

            for (int i = 0; i < smis.Count - 1; i++)
            {
                Smi smi = smis[i];
                if (smi.syncType != smis[i + 1].syncType)
                {
                    // 전후 싱크 타입이 맞을 때만 안전함
                    continue;
                }

                string lower = smi.text.ToLower();
                if (lower.IndexOf(" fade=") > 0)
                {
                    List <Color> fadeColors = new List <Color>();
                    List <Attr>  attrs      = smi.ToAttr();
                    for (int j = 0; j < attrs.Count; j++)
                    {
                        if (attrs[j].fade != 0)
                        {
                            string color = (attrs[j].fc.Length == 6) ? attrs[j].fc : "ffffff";
                            fadeColors.Add(new Color(j, attrs[j].fade > 0, color));
                            attrs[j].fade = 0;
                        }
                    }
                    if (fadeColors.Count == 0)
                    {
                        continue;
                    }

                    int start = smi.start, end = smis[i + 1].start;
                    int frames = (int)Math.Round((end - start) * 24 / 1001.0);

                    foreach (Color color in fadeColors)
                    {
                        Attr attr = attrs[color.index];
                        if (color.isIn)
                        {
                            attr.fc = color.Get(1, 2 * frames);
                        }
                        else
                        {
                            attr.fc = color.Get(2 * frames - 1, 2 * frames);
                        }
                    }
                    smi.FromAttr(attrs);
                    for (int j = 1; j < frames; j++)
                    {
                        foreach (Color color in fadeColors)
                        {
                            Attr attr = attrs[color.index];
                            if (color.isIn)
                            {
                                attr.fc = color.Get(1 + 2 * j, 2 * frames);
                            }
                            else
                            {
                                attr.fc = color.Get(2 * frames - (1 + 2 * j), 2 * frames);
                            }
                        }
                        smis.Insert(i + j, new Smi()
                        {
                            start = (start * (frames - j) + end * j) / frames,
                            //syncType = 2,
                            syncType = SyncType.inner
                        }.FromAttr(attrs));
                    }
                    i += frames - 1;
                }
                else if (lower.IndexOf(" typing=") > 0)
                {
                    // 타이핑은 한 싱크에 하나만 가능
                    int         attrIndex  = -1;
                    Attr        attr       = null;
                    List <Attr> attrs      = smi.ToAttr();
                    bool        isLastAttr = false;
                    for (int j = 0; j < attrs.Count; j++)
                    {
                        if (attrs[j].typing != null)
                        {
                            string color = (attrs[j].fc.Length == 6) ? attrs[j].fc : "ffffff";
                            attr = attrs[(attrIndex = j)];
                            string remains = "";
                            for (int k = j + 1; k < attrs.Count; k++)
                            {
                                remains += attrs[k].text;
                            }
                            isLastAttr = remains.Length == 0 || remains.StartsWith("\n");
                            if (!isLastAttr)
                            {
                                int length = 0;
                                for (int k = j + 1; k < attrs.Count; k++)
                                {
                                    length += attrs[k].text.Length;
                                }
                                isLastAttr = (length == 0);
                            }
                            break;
                        }
                    }
                    if (attr == null)
                    {
                        continue;
                    }

                    List <string> types = Typing.ToType(attr.text, attr.typing.mode, attr.typing.cursor);
                    float         width = GetLineWidth(attr.text);

                    int start = smi.start, end = smis[i + 1].start;
                    int count = types.Count - attr.typing.end - attr.typing.start;
                    if (count < 1)
                    {
                        continue;
                    }

                    int typingStart = attr.typing.start;
                    attr.typing = null;
                    smis.RemoveAt(i);
                    for (int j = 0; j < count; j++)
                    {
                        string text = types[j + typingStart];
                        attr.text = Width.GetAppend(GetLineWidth(text), width) + (isLastAttr ? "​" : "");

                        List <Attr> tAttrs = new List <Attr>();
                        tAttrs.AddRange(attrs.GetRange(0, attrIndex));
                        tAttrs.AddRange(new Smi()
                        {
                            text = text
                        }.ToAttr());
                        tAttrs.Add(attr);
                        if (!isLastAttr)
                        {
                            tAttrs.AddRange(attrs.GetRange(attrIndex + 1, attrs.Count - attrIndex - 1));
                        }

                        smis.Insert(i + j, new Smi()
                        {
                            start    = (start * (count - j) + end * (j)) / count,
                            syncType = j == 0 ? smi.syncType : SyncType.inner
                        }.FromAttr(tAttrs));
                    }
                    i += count - 1;
                }
            }
        }