/// <summary>
        /// 口形状タイムラインセットとモーフ情報セットから
        /// モーフ別タイムラインテーブルを作成する。
        /// </summary>
        /// <param name="tlSet">口形状タイムラインセット。</param>
        /// <param name="morphSet">モーフ情報セット。</param>
        /// <param name="morphEtoAI">
        /// "え" から "あ","い" へのモーフ変更を行うならば true 。
        /// </param>
        /// <returns>モーフ別タイムラインテーブル。</returns>
        public MorphTimelineTable Make(
            TimelineSet tlSet,
            MorphInfoSet morphSet,
            bool morphEtoAI)
        {
            var table = new MorphTimelineTable();

            foreach (var mt in tlSet)
            {
                var timeline = mt.Value;
                var morphInfo = morphSet[mt.Key];

                timeline.KeyAreas.ForEach(
                    area =>
                    {
                        foreach (var mw in morphInfo.MorphWeights)
                        {
                            AddMorphKeyArea(table, area, mw, morphEtoAI);
                        }
                    });
            }

            return table;
        }
Beispiel #2
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);
        }
Beispiel #3
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;
        }
Beispiel #4
0
        /// <summary>
        /// リップシンクユニット列挙を各々キー領域に変換してタイムラインに追加する。
        /// </summary>
        /// <param name="tlSet">追加先のタイムラインセット。</param>
        /// <param name="units">リップシンクユニット列挙。</param>
        private void AddAreaUnitLists(
            TimelineSet tlSet,
            IEnumerable<LipSyncUnit> units)
        {
            var areaUnits = new List<LipSyncUnit>();
            LipSyncUnit prevUnit = null;
            decimal place = 0;

            foreach (var unit in units)
            {
                // 次の音に入ったか?
                if (unit.LinkType.HasSingleSound())
                {
                    // ここまでの音をタイムラインに追加
                    place = AddAreaUnitList(tlSet, place, areaUnits, prevUnit, unit);
                    prevUnit = areaUnits.FirstOrDefault();
                    areaUnits.Clear();
                }

                // キー領域ユニット追加
                areaUnits.Add(unit);
            }

            // 最後の音をタイムラインに追加
            AddAreaUnitList(tlSet, place, areaUnits, prevUnit, null);
        }
Beispiel #5
0
        /// <summary>
        /// リップシンクユニット列挙から口形状種別ごとのタイムラインを作成する。
        /// </summary>
        /// <param name="units">リップシンクユニット列挙。</param>
        /// <returns>口形状種別ごとのタイムライン。</returns>
        public TimelineSet Make(IEnumerable<LipSyncUnit> units)
        {
            var tlSet = new TimelineSet();

            // タイムラインセット作成
            this.AddAreaUnitLists(tlSet, units);

            // 閉口タイムラインを修正
            this.FixClosedTimeline(tlSet);

            return tlSet;
        }