Split import script for Sony Vegas into two
`Import Rhubarb.cs` now only imports the animation. `Debug Rhubarb.cs` visualizes structured log data for debugging purposes.
This commit is contained in:
parent
04ca644cca
commit
4ac159dc4c
|
@ -0,0 +1,327 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.Design;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Design;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web.UI.Design;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows.Forms.Design;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using Sony.Vegas;
|
||||
using Region = Sony.Vegas.Region;
|
||||
|
||||
public class EntryPoint {
|
||||
public void FromVegas(Vegas vegas) {
|
||||
Config config = Config.Load();
|
||||
ImportDialog importDialog = new ImportDialog(config, delegate { Import(config, vegas); });
|
||||
importDialog.ShowDialog();
|
||||
config.Save();
|
||||
}
|
||||
|
||||
private void Import(Config config, Vegas vegas) {
|
||||
Project project = vegas.Project;
|
||||
|
||||
// Clear markers and regions
|
||||
if (config.ClearMarkers) {
|
||||
project.Markers.Clear();
|
||||
}
|
||||
if (config.ClearRegions) {
|
||||
project.Regions.Clear();
|
||||
}
|
||||
|
||||
// Load log file
|
||||
if (!File.Exists(config.LogFile)) {
|
||||
throw new Exception("Log file does not exist.");
|
||||
}
|
||||
Dictionary<EventType, List<TimedEvent>> timedEvents = ParseLogFile(config);
|
||||
|
||||
// Add markers/regions
|
||||
foreach (EventType eventType in timedEvents.Keys) {
|
||||
foreach (Visualization visualization in config.Visualizations) {
|
||||
if (visualization.EventType != eventType) continue;
|
||||
|
||||
List<TimedEvent> filteredEvents = FilterEvents(timedEvents[eventType], visualization.Regex);
|
||||
foreach (TimedEvent timedEvent in filteredEvents) {
|
||||
Timecode start = Timecode.FromSeconds(timedEvent.Start);
|
||||
Timecode end = Timecode.FromSeconds(timedEvent.End);
|
||||
switch (visualization.VisualizationType) {
|
||||
case VisualizationType.Marker:
|
||||
project.Markers.Add(new Marker(start, timedEvent.Value));
|
||||
break;
|
||||
case VisualizationType.Region:
|
||||
project.Regions.Add(new Region(start, end, timedEvent.Value));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<TimedEvent> FilterEvents(List<TimedEvent> timedEvents, Regex filterRegex) {
|
||||
if (filterRegex == null) return timedEvents;
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
Dictionary<int, TimedEvent> timedEventsByCharPosition = new Dictionary<int, TimedEvent>();
|
||||
foreach (TimedEvent timedEvent in timedEvents) {
|
||||
string inAngleBrackets = "<" + timedEvent.Value + ">";
|
||||
for (int charPosition = stringBuilder.Length;
|
||||
charPosition < stringBuilder.Length + inAngleBrackets.Length;
|
||||
charPosition++) {
|
||||
timedEventsByCharPosition[charPosition] = timedEvent;
|
||||
}
|
||||
stringBuilder.Append(inAngleBrackets);
|
||||
}
|
||||
|
||||
MatchCollection matches = filterRegex.Matches(stringBuilder.ToString());
|
||||
List<TimedEvent> result = new List<TimedEvent>();
|
||||
foreach (Match match in matches) {
|
||||
if (match.Length == 0) continue;
|
||||
|
||||
for (int charPosition = match.Index; charPosition < match.Index + match.Length; charPosition++) {
|
||||
TimedEvent matchedEvent = timedEventsByCharPosition[charPosition];
|
||||
if (!result.Contains(matchedEvent)) {
|
||||
result.Add(matchedEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Dictionary<EventType, List<TimedEvent>> ParseLogFile(Config config) {
|
||||
string[] lines = File.ReadAllLines(config.LogFile);
|
||||
Regex structuredLogLine = new Regex(@"##(\w+)\[(\d*\.\d*)-(\d*\.\d*)\]: (.*)");
|
||||
Dictionary<EventType, List<TimedEvent>> timedEvents = new Dictionary<EventType, List<TimedEvent>>();
|
||||
foreach (string line in lines) {
|
||||
Match match = structuredLogLine.Match(line);
|
||||
if (!match.Success) continue;
|
||||
|
||||
EventType eventType = (EventType) Enum.Parse(typeof(EventType), match.Groups[1].Value, true);
|
||||
double start = double.Parse(match.Groups[2].Value, CultureInfo.InvariantCulture);
|
||||
double end = double.Parse(match.Groups[3].Value, CultureInfo.InvariantCulture);
|
||||
string value = match.Groups[4].Value;
|
||||
|
||||
if (!timedEvents.ContainsKey(eventType)) {
|
||||
timedEvents[eventType] = new List<TimedEvent>();
|
||||
}
|
||||
timedEvents[eventType].Add(new TimedEvent(eventType, start, end, value));
|
||||
}
|
||||
return timedEvents;
|
||||
}
|
||||
}
|
||||
|
||||
public class TimedEvent {
|
||||
private readonly EventType eventType;
|
||||
private readonly double start;
|
||||
private readonly double end;
|
||||
private readonly string value;
|
||||
|
||||
public TimedEvent(EventType eventType, double start, double end, string value) {
|
||||
this.eventType = eventType;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public EventType EventType {
|
||||
get { return eventType; }
|
||||
}
|
||||
|
||||
public double Start {
|
||||
get { return start; }
|
||||
}
|
||||
|
||||
public double End {
|
||||
get { return end; }
|
||||
}
|
||||
|
||||
public string Value {
|
||||
get { return value; }
|
||||
}
|
||||
}
|
||||
|
||||
public class Config {
|
||||
private string logFile;
|
||||
private bool clearMarkers;
|
||||
private bool clearRegions;
|
||||
private List<Visualization> visualizations = new List<Visualization>();
|
||||
|
||||
[DisplayName("Log File")]
|
||||
[Description("A log file generated by Rhubarb Lip Sync.")]
|
||||
[Editor(typeof(FileNameEditor), typeof(UITypeEditor))]
|
||||
public string LogFile {
|
||||
get { return logFile; }
|
||||
set { logFile = value; }
|
||||
}
|
||||
|
||||
[DisplayName("Clear Markers")]
|
||||
[Description("Clear all markers in the current project.")]
|
||||
public bool ClearMarkers {
|
||||
get { return clearMarkers; }
|
||||
set { clearMarkers = value; }
|
||||
}
|
||||
|
||||
[DisplayName("Clear Regions")]
|
||||
[Description("Clear all regions in the current project.")]
|
||||
public bool ClearRegions {
|
||||
get { return clearRegions; }
|
||||
set { clearRegions = value; }
|
||||
}
|
||||
|
||||
[DisplayName("Visualization rules")]
|
||||
[Description("Specify how to visualize various log events.")]
|
||||
[Editor(typeof(CollectionEditor), typeof(UITypeEditor))]
|
||||
[XmlIgnore]
|
||||
public List<Visualization> Visualizations {
|
||||
get { return visualizations; }
|
||||
set { visualizations = value; }
|
||||
}
|
||||
|
||||
[Browsable(false)]
|
||||
public Visualization[] VisualizationArray {
|
||||
get { return visualizations.ToArray(); }
|
||||
set { visualizations = new List<Visualization>(value); }
|
||||
}
|
||||
|
||||
private static string ConfigFileName {
|
||||
get {
|
||||
string folder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||
return Path.Combine(folder, "DebugRhubarbSettings.xml");
|
||||
}
|
||||
}
|
||||
|
||||
public static Config Load() {
|
||||
try {
|
||||
XmlSerializer serializer = new XmlSerializer(typeof(Config));
|
||||
using (FileStream file = File.OpenRead(ConfigFileName)) {
|
||||
return (Config) serializer.Deserialize(file);
|
||||
}
|
||||
} catch (Exception) {
|
||||
return new Config();
|
||||
}
|
||||
}
|
||||
|
||||
public void Save() {
|
||||
XmlSerializer serializer = new XmlSerializer(typeof(Config));
|
||||
using (StreamWriter file = File.CreateText(ConfigFileName)) {
|
||||
XmlWriterSettings settings = new XmlWriterSettings();
|
||||
settings.Indent = true;
|
||||
settings.IndentChars = "\t";
|
||||
using (XmlWriter writer = XmlWriter.Create(file, settings)) {
|
||||
serializer.Serialize(writer, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Visualization {
|
||||
private EventType eventType;
|
||||
private string regexString;
|
||||
private VisualizationType visualizationType = VisualizationType.Marker;
|
||||
|
||||
[DisplayName("Event Type")]
|
||||
[Description("The type of event to visualize.")]
|
||||
public EventType EventType {
|
||||
get { return eventType; }
|
||||
set { eventType = value; }
|
||||
}
|
||||
|
||||
[DisplayName("Regular Expression")]
|
||||
[Description("A regular expression used to filter events. Leave empty to disable filtering.\nInput is a string of events in angle brackets. Example: '<AO>(?=<T>)' finds every AO phone followed by a T phone.")]
|
||||
public string RegexString {
|
||||
get { return regexString; }
|
||||
set { regexString = value; }
|
||||
}
|
||||
|
||||
[Browsable(false)]
|
||||
public Regex Regex {
|
||||
get { return string.IsNullOrEmpty(RegexString) ? null : new Regex(RegexString); }
|
||||
}
|
||||
|
||||
[DisplayName("Visualization Type")]
|
||||
[Description("Specify how to visualize events.")]
|
||||
public VisualizationType VisualizationType {
|
||||
get { return visualizationType; }
|
||||
set { visualizationType = value; }
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return string.Format("{0} -> {1}", EventType, VisualizationType);
|
||||
}
|
||||
}
|
||||
|
||||
public enum EventType {
|
||||
Utterance,
|
||||
Word,
|
||||
Phone,
|
||||
Shape
|
||||
}
|
||||
|
||||
public enum VisualizationType {
|
||||
None,
|
||||
Marker,
|
||||
Region
|
||||
}
|
||||
|
||||
public delegate void ImportAction();
|
||||
|
||||
public class ImportDialog : Form {
|
||||
private readonly Config config;
|
||||
private readonly ImportAction import;
|
||||
|
||||
public ImportDialog(Config config, ImportAction import) {
|
||||
this.config = config;
|
||||
this.import = import;
|
||||
SuspendLayout();
|
||||
InitializeComponent();
|
||||
ResumeLayout(false);
|
||||
}
|
||||
|
||||
private void InitializeComponent() {
|
||||
// Configure dialog
|
||||
Text = "Debug Rhubarb";
|
||||
Size = new Size(600, 400);
|
||||
Font = new Font(Font.FontFamily, 10);
|
||||
|
||||
// Add property grid
|
||||
PropertyGrid propertyGrid1 = new PropertyGrid();
|
||||
propertyGrid1.SelectedObject = config;
|
||||
Controls.Add(propertyGrid1);
|
||||
propertyGrid1.Dock = DockStyle.Fill;
|
||||
|
||||
// Add button panel
|
||||
FlowLayoutPanel buttonPanel = new FlowLayoutPanel();
|
||||
buttonPanel.FlowDirection = FlowDirection.RightToLeft;
|
||||
buttonPanel.AutoSize = true;
|
||||
buttonPanel.Dock = DockStyle.Bottom;
|
||||
Controls.Add(buttonPanel);
|
||||
|
||||
// Add Cancel button
|
||||
Button cancelButton1 = new Button();
|
||||
cancelButton1.Text = "Cancel";
|
||||
cancelButton1.DialogResult = DialogResult.Cancel;
|
||||
buttonPanel.Controls.Add(cancelButton1);
|
||||
CancelButton = cancelButton1;
|
||||
|
||||
// Add OK button
|
||||
Button okButton1 = new Button();
|
||||
okButton1.Text = "OK";
|
||||
okButton1.Click += OkButtonClickedHandler;
|
||||
buttonPanel.Controls.Add(okButton1);
|
||||
AcceptButton = okButton1;
|
||||
}
|
||||
|
||||
private void OkButtonClickedHandler(object sender, EventArgs e) {
|
||||
try {
|
||||
import();
|
||||
DialogResult = DialogResult.OK;
|
||||
} catch (Exception exception) {
|
||||
MessageBox.Show(exception.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -59,14 +59,6 @@ public class EntryPoint {
|
|||
project.Video.FieldOrder = VideoFieldOrder.ProgressiveScan;
|
||||
project.Video.PixelAspectRatio = 1;
|
||||
|
||||
// Add markers for phones
|
||||
XmlNodeList phoneElements = xmlDocument.SelectNodes("//phone");
|
||||
foreach (XmlElement phoneElement in phoneElements) {
|
||||
Timecode position = GetTimecode(phoneElement.Attributes["start"]);
|
||||
string label = phoneElement.InnerText;
|
||||
project.Markers.Add(new Marker(position, label));
|
||||
}
|
||||
|
||||
// Add video track with images
|
||||
VideoTrack videoTrack = vegas.Project.AddVideoTrack();
|
||||
foreach (XmlElement mouthCueElement in mouthCueElements) {
|
||||
|
@ -151,14 +143,18 @@ public class Config {
|
|||
private static string ConfigFileName {
|
||||
get {
|
||||
string folder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||
return Path.Combine(folder, "VegasRhubarbScriptSettings.xml");
|
||||
return Path.Combine(folder, "ImportRhubarbSettings.xml");
|
||||
}
|
||||
}
|
||||
|
||||
public static Config Load() {
|
||||
XmlSerializer serializer = new XmlSerializer(typeof(Config));
|
||||
using (FileStream file = File.OpenRead(ConfigFileName)) {
|
||||
return (Config) serializer.Deserialize(file);
|
||||
try {
|
||||
XmlSerializer serializer = new XmlSerializer(typeof(Config));
|
||||
using (FileStream file = File.OpenRead(ConfigFileName)) {
|
||||
return (Config) serializer.Deserialize(file);
|
||||
}
|
||||
} catch (Exception) {
|
||||
return new Config();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<ScriptSettings>
|
||||
<AssemblyReference>System.Design.dll</AssemblyReference>
|
||||
</ScriptSettings>
|
|
@ -0,0 +1,8 @@
|
|||
# Scripts for Sony Vegas
|
||||
|
||||
If you own a copy of [Sony Vegas](http://www.sonycreativesoftware.com/vegassoftware), you can use this script to visualize Rhubarb Lip Sync's output on the timeline. This can be useful for creating lip-synced videos or for debugging.
|
||||
|
||||
Copy (or symlink) the files in this directory to `<Vegas installation directory>\Script Menu`. When you restart Vegas, you'll find two new menu items:
|
||||
|
||||
* *Tools > Scripting > Import Rhubarb:* This will create a new Vegas project and add two tracks: a video track with a visualization of Rhubarb Lip-Sync's output and an audio track with the original recording.
|
||||
* *Tools > Scripting > Debug Rhubarb:* This will create markers or regions on the timeline visualizing Rhubarb Lip-Sync's internal data from a log file. You can obtain a log file by redirecting `stdout`. I've written this script mainly as a debugging aid for myself; feel free to contact me if you're interested and need a more thorough explanation.
|
|
@ -1,3 +0,0 @@
|
|||
# Import into Sony Vegas
|
||||
|
||||
If you own a copy of [Sony Vegas](http://www.sonycreativesoftware.com/vegassoftware), you can use this script to visualize Rhubarb Lip Sync's output on the timeline. Just copy (or symlink) `Import Rhubarb.cs` and `Import Rhubarb.cs.config` to `<Vegas installation directory>\Script Menu`. When you restart Vegas, you'll find a new menu item Tools > Scripting > Import Rhubarb.
|
Loading…
Reference in New Issue