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.FieldOrder = VideoFieldOrder.ProgressiveScan;
|
||||||
project.Video.PixelAspectRatio = 1;
|
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
|
// Add video track with images
|
||||||
VideoTrack videoTrack = vegas.Project.AddVideoTrack();
|
VideoTrack videoTrack = vegas.Project.AddVideoTrack();
|
||||||
foreach (XmlElement mouthCueElement in mouthCueElements) {
|
foreach (XmlElement mouthCueElement in mouthCueElements) {
|
||||||
|
@ -151,15 +143,19 @@ public class Config {
|
||||||
private static string ConfigFileName {
|
private static string ConfigFileName {
|
||||||
get {
|
get {
|
||||||
string folder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
string folder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||||
return Path.Combine(folder, "VegasRhubarbScriptSettings.xml");
|
return Path.Combine(folder, "ImportRhubarbSettings.xml");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Config Load() {
|
public static Config Load() {
|
||||||
|
try {
|
||||||
XmlSerializer serializer = new XmlSerializer(typeof(Config));
|
XmlSerializer serializer = new XmlSerializer(typeof(Config));
|
||||||
using (FileStream file = File.OpenRead(ConfigFileName)) {
|
using (FileStream file = File.OpenRead(ConfigFileName)) {
|
||||||
return (Config) serializer.Deserialize(file);
|
return (Config) serializer.Deserialize(file);
|
||||||
}
|
}
|
||||||
|
} catch (Exception) {
|
||||||
|
return new Config();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Save() {
|
public void Save() {
|
|
@ -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