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

693 lines
41 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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