/// <summary>
        /// 口形状キー領域をモーフウェイト値で変換してタイムラインへ追加する。
        /// </summary>
        /// <param name="table">追加先のタイムラインテーブル。</param>
        /// <param name="lipKeyArea">口形状キー領域。</param>
        /// <param name="morphWeight">モーフ名とそのウェイト値。</param>
        /// <param name="morphEtoAI">
        /// "え" から "あ","い" へのモーフ変更を行うならば true 。
        /// </param>
        private void AddMorphKeyArea(
            MorphTimelineTable table,
            KeyArea lipKeyArea,
            MorphWeightData morphWeight,
            bool morphEtoAI)
        {
            // "え" から "あ","い" への変換を行うか?
            bool e2ai = (morphEtoAI && morphWeight.MorphName == "え");

            // キー領域作成
            var area = new KeyArea();
            foreach (var p in lipKeyArea.Points)
            {
                area.AddPointAfter(
                    p.Key,
                    p.Value * morphWeight.Weight * (e2ai ? 0.5f : 1.0f));
            }

            // タイムラインに追加
            if (e2ai)
            {
                table.GetOrAddNew("あ").KeyAreas.Add(area);
                table.GetOrAddNew("い").KeyAreas.Add(area);
            }
            else
            {
                table.GetOrAddNew(morphWeight.MorphName).KeyAreas.Add(area);
            }
        }
Exemple #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 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;
        }