/// <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); } }
/// <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; }