Пример #1
0
        /// <summary>
        /// 閉口タイムラインを修正する。
        /// </summary>
        /// <param name="tlSet">修正対象のタイムラインセット。</param>
        private void FixClosedTimeline(TimelineSet tlSet)
        {
            // 閉口抜きのタイムライン配列作成
            var tlAiueo = (
                from it in tlSet
                where it.Key != LipId.Closed
                select it.Value)
                .ToArray();

            // 全キー位置と閉口以外のウェイト値合計のテーブル作成
            var points =
                new SortedList<decimal, float>(
                    tlSet.GetAllPlaces().ToDictionary(
                        p => p,
                        p => tlAiueo.Sum(tl => tl.GetWeight(p))));

            // 閉口タイムラインをクリア
            tlSet.Closed.KeyAreas.Clear();

            // 閉口タイムラインにキー領域を追加するデリゲート作成
            Action<TimelineKeyArea> areaAdder =
                a =>
                {
                    // キーが足りない or ウェイト値がすべて 0 なら追加しない
                    if (a.Points.Count < 2 || a.Points.All(pw => pw.Value <= 0))
                    {
                        return;
                    }

                    // エッジを閉口状態としないなら先頭と終端のウェイト値を 0 にする
                    if (!this.IsEdgeClosed)
                    {
                        a.Points[a.BeginPlace] = 0;
                        a.Points[a.EndPlace] = 0;
                    }

                    // 追加
                    tlSet.Closed.KeyAreas.Add(a);
                };

            var area = new TimelineKeyArea();

            // 閉口タイムラインを作成
            foreach (var pw in points)
            {
                // (1 - 閉口以外のウェイト値合計) を閉口ウェイト値とする
                var weight = Math.Max(0, 1 - pw.Value);

                if (weight > 0)
                {
                    // キー追加
                    area.SetWeight(pw.Key, weight);
                }
                else
                {
                    // 終端キー追加
                    area.SetWeight(pw.Key, 0);

                    // 閉口タイムラインにキー領域を追加
                    areaAdder(area);

                    // 新しいキー領域を作成開始
                    area = new TimelineKeyArea();
                    area.SetWeight(pw.Key, 0);
                }
            }

            // 閉口タイムラインに最後のキー領域を追加
            areaAdder(area);
        }
Пример #2
0
        /// <summary>
        /// ユニットリストをキー領域に変換してタイムラインに追加する。
        /// </summary>
        /// <param name="tlSet">追加先のタイムラインセット。</param>
        /// <param name="beginPlace">開始キー位置。</param>
        /// <param name="areaUnits">ユニットリスト。</param>
        /// <param name="prevUnit">先行発声ユニット。無いならば null 。</param>
        /// <param name="nextUnit">後続発声ユニット。無いならば null 。</param>
        /// <returns>後続のキー領域を開始すべきキー位置。</returns>
        private decimal AddAreaUnitList(
            TimelineSet tlSet,
            decimal beginPlace,
            IList<LipSyncUnit> areaUnits,
            LipSyncUnit prevUnit,
            LipSyncUnit nextUnit)
        {
            if (areaUnits.Count <= 0)
            {
                return beginPlace;
            }

            // 最初のユニットと口形状種別IDを取得
            var unit = areaUnits[0];
            var id = unit.LipId;

            var area = new TimelineKeyArea();

            // 開口および閉口の長さを算出
            var openCloseLen = CalcOpenCloseLength(unit);

            // 開始キーを追加
            var openHalfPos = beginPlace + openCloseLen / 2;
            if (prevUnit != null && unit.LinkType != LinkType.Normal)
            {
                // 一旦口を(半分)閉じるならば開口時間の1/2まで経過してから開口開始
                area.SetWeight(openHalfPos, 0);
            }
            else if (prevUnit != null && prevUnit.LipId == id)
            {
                // 直前と同じ口形状ならば開口時間の1/2まで経過してから開口開始
                // 接続ウェイト値は直前の口形状の終端部で設定済み
                area.SetWeight(openHalfPos, 0);
            }
            else
            {
                area.SetWeight(beginPlace, 0);
            }

            // 最大開口開始キーを追加
            var maxBeginPos = beginPlace + openCloseLen;
            area.SetWeight(maxBeginPos, 1);

            // 次の音開始位置(=最大開口終了位置)を算出
            var linkPos = maxBeginPos + CalcMaxOpenLength(unit);

            // 最初のユニットをカット
            var units = areaUnits.Skip(1);

            // 次のユニットを取得
            unit = units.FirstOrDefault();

            // 最大開口終端位置追加済みフラグ
            bool maxEndAdded = false;

            // 長音部分を計算
            if (unit != null && unit.LinkType == LinkType.LongSound)
            {
                // 次の音開始位置をシフト
                foreach (
                    var u in units.TakeWhile(u => u.LinkType == LinkType.LongSound))
                {
                    linkPos += CalcUnitLength(u);
                }

                // 長音終端キーを追加
                area.SetWeight(linkPos, this.LongSoundLastWeight);
                maxEndAdded = true;

                // 長音部分をカット
                units = units.SkipWhile(u => u.LinkType == LinkType.LongSound);

                // 次のユニットを取得
                unit = units.FirstOrDefault();
            }

            // 促音部分を計算
            if (unit != null && unit.LinkType == LinkType.Tsu)
            {
                // 次の音開始位置をシフト
                foreach (
                    var u in units.TakeWhile(u => u.LinkType == LinkType.Tsu))
                {
                    linkPos += CalcUnitLength(u);
                }

                // 促音終端キーを追加
                area.SetWeight(linkPos, area.Points.Last().Value);
                maxEndAdded = true;

                // 長音部分をカット
                units = units.SkipWhile(u => u.LinkType == LinkType.Tsu);

                // 次のユニットを取得
                unit = units.FirstOrDefault();
            }

            // 最大開口終端キーが追加されていなければ追加
            if (!maxEndAdded)
            {
                area.SetWeight(linkPos, area.Points.Last().Value);
            }

            // 継続部分を計算
            // 継続より後ろの長音や促音も継続扱い
            if (unit != null)
            {
                // 次の音開始位置をシフト
                foreach (var u in units)
                {
                    linkPos += CalcUnitLength(u);
                }
            }

            // 終端キーを追加
            var endPos = linkPos + openCloseLen;
            if (nextUnit != null && nextUnit.LinkType != LinkType.Normal)
            {
                // 一旦口を閉じるならば次の音の開口時間の1/2だけ終端を早める
                var closeHalfPos = endPos - CalcOpenCloseLength(nextUnit) / 2;
                closeHalfPos = Math.Max(closeHalfPos, linkPos);
                area.SetWeight(closeHalfPos, 0);
            }
            else if (
                nextUnit != null &&
                nextUnit.LipId == id &&
                nextUnit.LinkType == LinkType.Normal)
            {
                // 後続と同じ口形状の場合

                // 次の音の開口時間の1/2だけ終端を早める
                var closeHalfPos = endPos - CalcOpenCloseLength(nextUnit) / 2;
                closeHalfPos = Math.Max(closeHalfPos, linkPos);

                // 接続ウェイト値を終端値とする
                // 最大開口終端キーのウェイト値に比例させる(長音対応)
                var weight = DecideSameLipLinkWeight(areaUnits[0], nextUnit);
                weight *= area.Points.Last().Value;

                area.SetWeight(closeHalfPos, weight);
            }
            else
            {
                area.SetWeight(endPos, 0);
            }

            // タイムラインに追加
            tlSet[id].KeyAreas.Add(area);

            // 次の音の開始位置を返す
            return linkPos;
        }