/* * RspImporter.cs * Copyright (c) 2007-2009 kbinani * * This file is part of LipSync. * * LipSync is free software; you can redistribute it and/or * modify it under the terms of the BSD License. * * LipSync is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Web; using System.Xml; using Boare.Lib.AppUtil; namespace LipSync { class RspTriggerEvent : IComparable{ public float time; public string triggerName; public RspTriggerStatus status; public RspTriggerEvent( float time, string triggerName, string status ){ this.time = time; this.triggerName = triggerName.Trim(); if( status.Trim() == "ON" ){ this.status = RspTriggerStatus.ON; }else{ this.status = RspTriggerStatus.OFF; } } public int CompareTo( object obj ) { RspTriggerEvent item = (RspTriggerEvent)obj; if ( this.time > item.time ) { return 1; } else if ( this.time < item.time ) { return -1; } else { if ( this.status == RspTriggerStatus.OFF && item.status == RspTriggerStatus.ON ) { return -1; } else if ( this.status == RspTriggerStatus.ON && item.status == RspTriggerStatus.OFF ) { return 1; } return 0; } } new public string ToString() { return time.ToString() + "," + triggerName + "," + status.ToString(); } } enum RspTriggerStatus{ ON, OFF, } public class RspImporter { private static XmlDocument doc; private static TimeTableGroup group_vsq; private static List groups_character; private static TimeTableGroup group_another; private static List telop = new List(); private static float optional_length; private static int width; private static int height; private static int back_color; private static string sound_file; public static string gettext( string s ) { return Messaging.GetMessage( s ); } public static string _( string s ) { return gettext( s ); } public static bool Import( string filepath, ref SettingsEx save ) { string rsp_path = Path.GetDirectoryName( filepath ); doc = new XmlDocument(); doc.Load( filepath ); group_vsq = new TimeTableGroup( _( "VSQ Tracks" ), 0, null ); groups_character = new List(); group_another = new TimeTableGroup( _( "Another images" ), 0, null ); // 最初にtotal_lengthを取得 foreach( XmlNode level1 in doc.DocumentElement.ChildNodes ){ if( level1.LocalName == "View" ){ optional_length = -1f; foreach ( XmlNode level2 in level1.ChildNodes ) { if ( level2.LocalName == "OptionalLength" ) { optional_length = float.Parse( level2.InnerText ); break; } } break; } } foreach ( XmlNode level1 in doc.DocumentElement.ChildNodes ) { switch ( level1.LocalName ) { case "Canvas": #region Canvas width = 100; height = 100; back_color = -1; foreach ( XmlNode level2 in level1.ChildNodes ) { switch ( level2.LocalName ) { case "Width": width = int.Parse( level2.InnerText ); break; case "Height": height = int.Parse( level2.InnerText ); break; case "BackColor": back_color = int.Parse( level2.InnerText ); break; } } #endregion break; case "Sound": #region Sound sound_file = ""; foreach ( XmlNode level2 in level1.ChildNodes ) { switch ( level2.LocalName ) { case "FileName": sound_file = level2.InnerText; break; } } #endregion break; case "TimeTable": #region TimeTable foreach ( XmlNode level2 in level1.ChildNodes ) { if ( level2.LocalName == "Table" ) { group_vsq.Add( new TimeTable( "", 0, TimeTableType.vsq, null ) ); int index = group_vsq.Count - 1; foreach ( XmlNode level3 in level2.ChildNodes ) { switch ( level3.LocalName ) { case "Name": group_vsq[index].Text = level3.InnerText; break; case "Unit": float start = 0f; float end = 0f; string lyric = ""; string symbol = ""; foreach ( XmlNode level4 in level3.ChildNodes ) { switch ( level4.LocalName ) { case "Start": start = float.Parse( level4.InnerText ); break; case "End": end = float.Parse( level4.InnerText ); break; case "Text": lyric = level4.InnerText; break; case "Symbol": symbol = level4.InnerText; break; } } group_vsq[index].Add( new TimeTableEntry( start, end, lyric + "(" + symbol + ")" ) ); break; } } } } #endregion break; case "DrawObject": #region DrawObject foreach ( XmlNode level2 in level1.ChildNodes ) { if ( level2.LocalName == "Object" ) { // まずtypeを調べる string type_name = ""; foreach ( XmlNode level3 in level2.ChildNodes ) { if ( level3.LocalName == "TypeName" ) { type_name = level3.InnerText; break; } } // typeで動作を切り替え switch ( type_name ) { case "RipSync.Plugins.Extensions.RipSyncImage.RipSyncImage": #region RipSync.Plugins.Extensions.RipSyncImage.RipSyncImage string source_table = ""; CharacterEx cex = new CharacterEx(); List events = new List(); groups_character.Add( new TimeTableGroup( "", 0, null ) ); int index = groups_character.Count - 1; foreach ( XmlNode level3 in level2.ChildNodes ) { switch( level3.LocalName ){ case "Name": groups_character[index].Text = level3.InnerText; break; case "Parameter": //MessageBox.Show( "Parameter node" ); foreach( XmlNode level4 in level3.ChildNodes ){ switch( level4.LocalName ){ case "FileName": string rsi_path = Path.Combine( rsp_path, level4.InnerText ); cex = RsiReader.Read( rsi_path ); groups_character[index].Character = cex.character; break; case "Table": source_table = level4.InnerText; break; case "Triggers": foreach( XmlNode level5 in level4.ChildNodes ){ if ( level5.LocalName == "TriggerEvent" ) { float time = 0f; string trigger_name = ""; string status = ""; foreach ( XmlNode level6 in level5.ChildNodes ) { switch ( level6.LocalName ) { case "Time": time = float.Parse( level6.InnerText ); break; case "TriggerName": trigger_name = level6.InnerText; break; case "Status": status = level6.InnerText; break; } } events.Add( new RspTriggerEvent( time, trigger_name, status ) ); } } break; } } break; } } // source_tableから、口パクを作成 // group_vsqから、タイトルがsource_tableで或るやつを検索 int source = -1; for ( int i = 0; i < group_vsq.Count; i++ ) { if ( group_vsq[i].Text == source_table ) { source = i; break; } } if ( source >= 0 ) { // 口パク用のタイムテーブルが指定されていた場合 TimeTableGroup temp = (TimeTableGroup)groups_character[index].Clone(); TimeTableGroup.GenerateLipSyncFromVsq( group_vsq[source], ref temp, groups_character[index].Character, optional_length, true, save.FrameRate, 0f ); //todo: ここのoptional_lengthが怪しい groups_character[index] = temp; } else { //口パク用タイムテーブルが指定されていなかった場合 // 画像分だけトラックを用意 /*groups_character[index].Add( new TimeTable( "base", 0, TimeTableType.eye, cex.character.Base ) ); groups_character[index].Add( new TimeTable( "a", 0, TimeTableType.mouth, cex.character.a ) ); groups_character[index].Add( new TimeTable( "aa", 0, TimeTableType.mouth, cex.character.aa ) ); groups_character[index].Add( new TimeTable( "i", 0, TimeTableType.mouth, cex.character.i ) ); groups_character[index].Add( new TimeTable( "u", 0, TimeTableType.mouth, cex.character.u ) ); groups_character[index].Add( new TimeTable( "e", 0, TimeTableType.mouth, cex.character.e ) ); groups_character[index].Add( new TimeTable( "o", 0, TimeTableType.mouth, cex.character.o ) ); groups_character[index].Add( new TimeTable( "xo", 0, TimeTableType.mouth, cex.character.xo ) ); groups_character[index].Add( new TimeTable( "nn", 0, TimeTableType.mouth, cex.character.nn ) );*/ for ( int k = 0; k < groups_character[index].Character.Count; k++ ) { groups_character[index].Add( new TimeTable( groups_character[index].Character[k].title, 0, TimeTableType.character, null ) ); } } // triggerから、表情を追加。 // まず、排他も考慮して、OFFのイベントを追加 events.Sort(); //MessageBox.Show( "events.Cocunt=" + events.Count ); #region 排他を考慮して、OFFイベントを追加 int ii = -1; while( (ii + 1) < events.Count ){ ii++; //for ( int i = 0; i < events.Count; i++ ) { string trigger_name = events[ii].triggerName; //MessageBox.Show( "trigger_name=" + trigger_name ); float begin = events[ii].time; float end = begin - 10f; // begin > endであれば-10じゃなくてもいい // 排他に該当して無いかどうか検査 bool exclusion = isExclusionTrigger( cex, trigger_name ); bool defined = false; // 排他制御で、OFF位置が定義できたか否か if ( exclusion ) { // 排他制御をする必要がある場合 // 他の排他要素の中で、beginより後にONしていて、かつそれがもっとも早いものを検索 float first_begin = end; //bool int_exclusion = false; for ( int j = ii + 1; j < events.Count; j++ ) { if ( isExclusionTrigger( cex, events[j].triggerName ) ) { defined = true; end = events[j].time; break; } } } //MessageBox.Show( "defined=" + defined ); if ( !defined ) { // この後で同じトリガが再びON指定になってたり、作為的にOFFにされて無いかどうかを検査 for ( int j = ii + 1; j < events.Count; j++ ) { if ( events[j].triggerName == trigger_name ) { end = events[j].time; defined = true; break; } } } if ( defined ) { events.Add( new RspTriggerEvent( end, trigger_name, "OFF" ) ); events.Sort(); ii++; //++しないと∞ループだな } } #endregion for ( int i = 0; i < events.Count; i++ ) { float begin, end; if ( events[i].status == RspTriggerStatus.ON ){ // まず、開始と終了の時刻を設定 begin = events[i].time; end = -1f; if ( i + 1 < events.Count ) { if ( events[i + 1].triggerName == events[i].triggerName && events[i + 1].status == RspTriggerStatus.OFF ) { end = events[i + 1].time; } } // どのタイムラインに追加するのかを決定 string trigger = events[i].triggerName; int target = -1; for ( int j = 0; j < groups_character[index].Count; j++ ) { if ( groups_character[index][j].Text == trigger ) { target = j; break; } } // 追加を実行 if ( target >= 0 ) { groups_character[index][target].Add( new TimeTableEntry( begin, end, trigger ) ); } } } #endregion break; case "RipSync.Plugins.Extensions.SimpleImage.SimpleImage": #region RipSync.Plugins.Extensions.SimpleImage.SimpleImage string image_file = ""; string name = ""; foreach ( XmlNode level3 in level2.ChildNodes ) { switch ( level3.LocalName ) { case "Parameter": foreach ( XmlNode level4 in level3.ChildNodes ) { if ( level4.LocalName == "FileName" ) { image_file = level3.InnerText; break; } } break; case "Name": name = level3.InnerText; break; } } image_file = HttpUtility.UrlDecode( image_file ); string file = Path.Combine( rsp_path, image_file ); if ( File.Exists( file ) ) { Bitmap img = new Bitmap( Common.ImageFromFile( file ) ); group_another.Add( new TimeTable( name, 0, TimeTableType.another, img ) ); } else { group_another.Add( new TimeTable( name, 0, TimeTableType.another, null ) ); } #endregion break; case "RipSync.Plugins.Extensions.TextObject.TextObject": #region RipSync.Plugins.Extensions.TextObject.TextObject string tname = ""; string text = ""; Color color = Color.Black; Font font = new Font( "MS UI Gothc", 10 ); foreach ( XmlNode level3 in level2.ChildNodes ) { switch ( level3.LocalName ) { case "Name": tname = level3.InnerText; break; case "Parameter": foreach ( XmlNode level4 in level3.ChildNodes ) { switch ( level4.LocalName ) { case "Text": text = level4.InnerText; break; case "Color": int col = int.Parse( level4.InnerText ); color = Color.FromArgb( col ); break; case "Font": string font_name = level4.InnerText; //Microsoft Sans Serif, 12pt, style=Bold string[] spl = font_name.Split( new string[] { ", " }, StringSplitOptions.RemoveEmptyEntries ); string font_family = spl[0]; string font_size = spl[1]; font_size = font_size.Replace( "pt", "" ); int em_size = int.Parse( font_size ); string font_style = ""; //style=Bold, Italic, Underline, Strikeout FontStyle t_font_style = FontStyle.Regular; if ( spl.Length >= 3 ) { //spl[2]のみ、Style=がついてるのでそれをはずす string[] spl2 = spl[2].Split( new string[] { "=" }, StringSplitOptions.RemoveEmptyEntries ); font_style = spl2[1]; } for ( int ic = 3; ic < spl.Length; ic++ ) { font_style += "," + spl[ic]; } spl = font_style.Split( new string[] { "," }, StringSplitOptions.RemoveEmptyEntries ); foreach ( string style_name in spl ) { switch ( style_name ) { case "Bold": t_font_style = t_font_style | FontStyle.Bold; break; case "Italic": t_font_style = t_font_style | FontStyle.Italic; break; case "Underline": t_font_style = t_font_style | FontStyle.Underline; break; case "Strikeout": t_font_style = t_font_style | FontStyle.Strikeout; break; } } font = new Font( font_family, em_size, t_font_style ); break; } } break; } } Telop temp_telop = new Telop( save.GetNextID() ); temp_telop.Text = text; temp_telop.Color = color; temp_telop.Font = font; temp_telop.Tag = tname; telop.Add( temp_telop ); #endregion break; } } } #endregion break; case "TimeLine": #region TimeLine foreach ( XmlNode level2 in level1.ChildNodes ) { if ( level2.LocalName == "Unit" ) { int zindex = 0; float start = 0f; float end = -10f; int position_x = 0; int position_y = 0; float scale = 1f; string draw_object = ""; bool fadein = false; bool fadeout = false; foreach ( XmlNode level3 in level2.ChildNodes ) { switch ( level3.LocalName ) { case "ZIndex": zindex = int.Parse( level3.InnerText ); break; case "Start": start = float.Parse( level3.InnerText ); break; case "End": end = float.Parse( level3.InnerText ); break; case "PositionX": position_x = int.Parse( level3.InnerText ); break; case "PositionY": position_y = int.Parse( level3.InnerText ); break; case "Scale": scale = float.Parse( level3.InnerText ); break; case "DrawObject": draw_object = level3.InnerText; break; case "FadeIn": if( level3.InnerText == "True" ){ fadein = true; } break; case "FadeOut": if( level3.InnerText == "True" ){ fadeout = true; } break; } } // 名前がdraw_objectであるトラック(orキャラクタ)を検索 // group_another for ( int i = 0; i < group_another.Count; i++ ) { if ( draw_object == group_another[i].Text ) { group_another[i].ZOrder = zindex; group_another[i].Clear(); group_another[i].Add( new TimeTableEntry( start, end, draw_object ) ); group_another[i].Position = new Point( position_x, position_y ); group_another[i].Scale = scale; } } // group_character for ( int i = 0; i < groups_character.Count; i++ ) { if ( draw_object == groups_character[i].Text ) { groups_character[i].ZOrder = zindex; groups_character[i].Position = new Point( position_x, position_y ); } } // telop for ( int i = 0; i < telop.Count; i++ ) { if ( draw_object == (string)telop[i].Tag ) { telop[i].Start = start; telop[i].End = end; telop[i].FadeIn = fadein; telop[i].FadeOut = fadeout; telop[i].Position = new Point( position_x, position_y ); if ( scale != 1 ) { Font tnew = new Font( telop[i].Font.FontFamily, telop[i].Font.SizeInPoints * scale, telop[i].Font.Style ); telop[i].Font.Dispose(); telop[i].Font = null; telop[i].Font = tnew; } } } } } #endregion break; } } //MessageBox.Show( "tables=" + group_vsq.Count ); if ( optional_length < 0 ) { float thismax = -1; for ( int track = 0; track < group_vsq.Count; track++ ) { int last = group_vsq[track].Count - 1; if ( last >= 0 ) { thismax = group_vsq[track][last].end; optional_length = Math.Max( optional_length, thismax ); } } for ( int group = 0; group < groups_character.Count; group++ ) { for ( int track = 0; track < groups_character[group].Count; track++ ) { int last = groups_character[group][track].Count - 1; if ( last >= 0 ) { thismax = groups_character[group][track][last].end; optional_length = Math.Max( optional_length, thismax ); } } } } // 時間が負になってる表情用エントリを整理 for ( int group = 0; group < groups_character.Count; group++ ) { for ( int track = 0; track < groups_character[group].Count; track++ ) { for( int entry = 0; entry < groups_character[group][track].Count; entry++ ){ if ( groups_character[group][track][entry].end < 0 ) { groups_character[group][track][entry].end = optional_length; } } } } // 時間が負になっているその他のイメージのエントリを整理 for ( int track = 0; track < group_another.Count; track++ ) { for ( int entry = 0; entry < group_another[track].Count; entry++ ) { if ( group_another[track][entry].end < 0f ) { group_another[track][entry].end = optional_length; } } } // 時間が負になっているテロップのエントリを整理 for ( int i = 0; i < telop.Count; i++ ) { if ( telop[i].End < 0f ) { telop[i].End = optional_length; } } // base画像エントリの修正。genMouthFromVsqCore実行時にはoptional_length == 0なので、修正。 for ( int group = 0; group < groups_character.Count; group++ ) { int index_base = -1; for ( int track = 0; track < groups_character[group].Count; track++ ) { if ( groups_character[group][track].Text == "base" ) { index_base = track; break; } } if ( index_base >= 0 ) { groups_character[group][index_base].Clear(); groups_character[group][index_base].Add( new TimeTableEntry( 0.0f, optional_length, "base" ) ); } } // zorderの数値がrip syncでは逆なので修正 // まずzorderの最大値を探す int zmax = -100; for ( int i = 0; i < group_another.Count; i++ ) { zmax = Math.Max( zmax, group_another[i].ZOrder ); } for ( int i = 0; i < groups_character.Count; i++ ) { zmax = Math.Max( zmax, groups_character[i].ZOrder ); } // 修正 for ( int i = 0; i < group_another.Count; i++ ) { group_another[i].ZOrder = zmax - group_another[i].ZOrder; } for ( int i = 0; i < groups_character.Count; i++ ) { groups_character[i].ZOrder = zmax - groups_character[i].ZOrder; } if ( optional_length > 0 ) { save.m_totalSec = optional_length; } else { save.m_totalSec = 0; } save.m_group_vsq = (TimeTableGroup)group_vsq.Clone(); save.m_groups_character.Clear(); for ( int i = 0; i < groups_character.Count; i++ ) { save.m_groups_character.Add( groups_character[i] ); } save.m_group_another = (TimeTableGroup)group_another.Clone(); save.m_movieSize = new Size( width, height ); save.m_telop_ex2.Clear(); save.m_telop_ex2 = new List(); for ( int i = 0; i < telop.Count; i++ ) { save.m_telop_ex2.Add( telop[i] ); } save.UpdateZorder(); save.m_audioFile = sound_file; //s.InvertZOrder(); return true; } /// /// 指定された名前のトリガーが、排他指定されているか否かを判定します /// /// 判定に使用されるCharacterEx /// 検査するトリガーの名前 /// private static bool isExclusionTrigger( CharacterEx cex, string name ) { for ( int i = 0; i < cex.exclusion.Count; i++ ) { if ( cex.exclusion[i] == name ) { return true; } } return false; } } }