lipsync/trunk/LipSync/Editor/RipSync/RspImporter.cs

693 lines
41 KiB
C#
Raw Normal View History

/*
* 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<TimeTableGroup> groups_character;
private static TimeTableGroup group_another;
private static List<Telop> telop = new List<Telop>();
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<TimeTableGroup>();
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<RspTriggerEvent> events = new List<RspTriggerEvent>();
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<Telop>();
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;
}
/// <summary>
/// 指定された名前のトリガーが、排他指定されているか否かを判定します
/// </summary>
/// <param name="cex">判定に使用されるCharacterEx</param>
/// <param name="name">検査するトリガーの名前</param>
/// <returns></returns>
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;
}
}
}