コード例 #1
0
        /// <summary>
        /// 口形状タイムラインセットとモーフ情報セットから
        /// モーフ別タイムラインテーブルを作成する。
        /// </summary>
        /// <param name="tlSet">口形状タイムラインセット。</param>
        /// <param name="morphSet">モーフ情報セット。</param>
        /// <param name="morphEtoAI">
        /// "え" から "あ","い" へのモーフ変更を行うならば true 。
        /// </param>
        /// <returns>モーフ別タイムラインテーブル。</returns>
        public MorphTimelineTable Make(
            LipTimelineSet tlSet,
            LipMorphSet 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 => morphInfo.MorphWeights.ForEach(
                        mw => AddMorphKeyArea(table, area, mw, morphEtoAI)));
            }

            return table;
        }
コード例 #2
0
ファイル: LipTimelineMaker.cs プロジェクト: ruche7/ruche.mmm
        /// <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 long AddAreaUnitList(
            LipTimelineSet tlSet,
            long 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 KeyArea();

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

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

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

            // 次の音開始位置(=最大開口終了位置)を算出
            long 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);
                }

                // 長音終端キーを追加
                linkPos = area.AddPointAfter(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);
                }

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

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

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

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

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

            // 終端キーを追加
            long endPos = linkPos + openCloseLen;
            if (nextUnit != null && nextUnit.LinkType == LinkType.PreClose)
            {
                // 一旦口を閉じるならば次の音の開口時間の1/2だけ終端を早める
                long closeHalfPos = endPos - CalcOpenCloseLength(nextUnit) / 2;
                closeHalfPos = Math.Max(closeHalfPos, linkPos);
                area.AddPointAfter(closeHalfPos, 0);
            }
            else if (
                nextUnit != null &&
                nextUnit.LipId == id &&
                nextUnit.LinkType == LinkType.Normal)
            {
                // 後続と同じ口形状ならば接続ウェイト値の1/2を設定
                long closeHalfPos = endPos - CalcOpenCloseLength(nextUnit) / 2;
                closeHalfPos = Math.Max(closeHalfPos, linkPos);
                var lw = DecideSameLipLinkWeight(unit, nextUnit);
                area.AddPointAfter(closeHalfPos, lw / 2);
            }
            else
            {
                area.AddPointAfter(endPos, 0);
            }

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

            // 次の音の開始位置を返す
            return linkPos;
        }
コード例 #3
0
ファイル: LipTimelineMaker.cs プロジェクト: ruche7/ruche.mmm
        /// <summary>
        /// リップシンクユニットリストから口形状種別ごとのタイムラインを作成する。
        /// </summary>
        /// <param name="units">リップシンクユニットリスト。</param>
        /// <returns>口形状種別ごとのタイムライン。</returns>
        public LipTimelineSet Make(IEnumerable<LipSyncUnit> units)
        {
            var tlSet = new LipTimelineSet();
            var areaUnits = new List<LipSyncUnit>();
            LipSyncUnit prevUnit = null;
            long 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);

            return tlSet;
        }