JD2022-TU1/main/tools/apps/JDSongConverter/Tasks/ConvertTimelineToKtapeTask.cs

233 lines
10 KiB
C#

using System;
using System.IO;
using System.Xml.XPath;
using System.Linq;
using System.Xml.Linq;
using JD.Asset.Template.Utils;
using JD.Collections.Extensions;
using JD.Serialization;
using JDSongConverter.Utils;
using TeaBox.Macros;
using FrameConverter = JD.Core.FrameConverter;
using JD.Timeline.Model;
namespace JDSongConverter.Tasks
{
/// <summary>
/// Converts different deprecated timeline files to karaoke tape file
/// </summary>
public class ConvertTimelineToKtapeTask : Task
{
public string TimelineFile { get; set; }
public string KaraokeTapeFile { get; set; }
public string LyricsFile { get; set; }
public void Init(string timelineFile, string karaokeTapeFile, string lyricsFile)
{
TimelineFile = timelineFile;
KaraokeTapeFile = karaokeTapeFile;
LyricsFile = lyricsFile;
}
public override string Name
{
get { return "Convert timeline to karaoke tape"; }
set { }
}
public override string ProgressText
{
get { return "Converting timeline to karaoke tape"; }
}
public override TaskResult Process(IExecutionContext executionContext)
{
if (!File.Exists(TimelineFile))
return TaskResult.FileMissing;
var tape = new Tape();
if (TimelineFile.EndsWith(".tpl", StringComparison.CurrentCultureIgnoreCase))
{
ImportTpl(tape, TimelineFile);
}
else if (TimelineFile.EndsWith(".tml", StringComparison.CurrentCultureIgnoreCase))
{
ImportTml(tape, TimelineFile);
}
tape.Clips.OfType<KaraokeClip>().Where(x => x.ContentType == KaraokeClip.ContentType_Enum.KaraokeContent_LyricsOnly).FixCollisions();
tape.Save(KaraokeTapeFile);
UpdateLyricsFile(tape);
return TaskResult.Ok;
}
private void ImportTpl(Tape tape, string fileName)
{
var serializer = new LuaSerializer();
var actorTemplate = (Actor_Template)serializer.LoadObjectFromFile(fileName);
if (actorTemplate == null)
return;
JD_Timeline_Template timeline = actorTemplate.COMPONENTS.OfType<JD_Timeline_Template>().FirstOrDefault();
if (timeline == null)
return;
tape.Id = KaraokeTapeFile;
tape.IsLoading = true;
tape.TapeClock = Tape.TapeClock_Enum.TapeClock_ConductorGameplay;
tape.MapName = Context.Song.Name;
//We read karaoke scoring general parameters
var karaokeInfos = new KaraokeMetaInfo
{
Id = IdGenerator.Instance.Generate(),
HistorySpan = timeline.KaraokeScoringData.Params.HistorySpan,
NoPitchScoring = timeline.KaraokeScoringData.Params.NoPitchScoring,
PitchLowerBound = timeline.KaraokeScoringData.Params.PitchLowerBound,
PitchUpperBound = timeline.KaraokeScoringData.Params.PitchUpperBound,
RMSTendencyToDecrease = timeline.KaraokeScoringData.Params.RMSTendencyToDecrease,
RMSTendencyToIncrease = timeline.KaraokeScoringData.Params.RMSTendencyToIncrease,
RMSThreshold = timeline.KaraokeScoringData.Params.RMSThreshold,
SlidingMeanVariationThreshold = timeline.KaraokeScoringData.Params.SlidingMeanVariationThreshold,
SlidingMeanWindowSize = timeline.KaraokeScoringData.Params.SlidingMeanWindowSize,
WindowHop = timeline.KaraokeScoringData.Params.WindowHop,
WindowSize = timeline.KaraokeScoringData.Params.WindowSize,
YinPitchTreshold = timeline.KaraokeScoringData.Params.YINpitchThreshold,
};
tape.MetaInfos.Add(karaokeInfos);
//We read karaoke notes
foreach (var signalAnnotation in timeline.KaraokeScoringData.Vect)
{
var clip = new KaraokeClip
{
Id = IdGenerator.Instance.Generate(),
StartBeat = tape.TimeInBeats(signalAnnotation.StartTime),
BeatDuration = tape.TimeInBeats(signalAnnotation.EndTime) - tape.TimeInBeats(signalAnnotation.StartTime),
Pitch = signalAnnotation.Pitch,
ContentType = KaraokeClip.ContentType_Enum.KaraokeContent_Singing,
StartTimeTolerance = (int)FrameConverter.FrameFromTime(Math.Abs(tape.TimeInBeats(signalAnnotation.Tolerance.StartTime) - tape.TimeInBeats(signalAnnotation.StartTime))),
EndTimeTolerance = (int)FrameConverter.FrameFromTime(Math.Abs(tape.TimeInBeats(signalAnnotation.Tolerance.EndTime) - tape.TimeInBeats(signalAnnotation.EndTime))),
SemitoneTolerance = signalAnnotation.Tolerance.SemitoneTolerance,
TrackId = 0
};
tape.Clips.Add(clip);
}
//We read lyrics
foreach (var lyricsChunk in timeline.Lyrics)
{
var clip = new KaraokeClip
{
Id = IdGenerator.Instance.Generate(),
Lyrics = lyricsChunk.Text,
StartBeat = lyricsChunk.StartPosition,
BeatDuration = lyricsChunk.StopPosition - lyricsChunk.StartPosition,
ContentType = KaraokeClip.ContentType_Enum.KaraokeContent_LyricsOnly,
IsEndOfLine = (lyricsChunk.IsLineEnding > 0),
MidiNote = 0,
TrackId = 0
};
tape.Clips.Add(clip);
}
}
private void ImportTml(Tape tape, string fileName)
{
tape.Id = KaraokeTapeFile;
tape.IsLoading = true;
tape.TapeClock = Tape.TapeClock_Enum.TapeClock_ConductorGameplay;
tape.MapName = Context.Song.Name;
XDocument xmlFile = XDocument.Load(fileName);
var layers = xmlFile.XPathSelectElements("/JustDanceToolLD/partition/layer").ToList();
//We read lyrics
var layerNamedLyrics = layers.FirstOrDefault(x => x.Attribute("name").Value == "Lyrics");
var lyricsClips = new UAFList<KaraokeClip>();
if (layerNamedLyrics != null)
{
foreach (var instance in layerNamedLyrics.Descendants("instance"))
{
int position = Int32.Parse(instance.Attribute("position").Value)-1;
var offset = (float)XmlUtils.ReadDoubleValue(instance.Element("Offset"), 0);
var length = (float)XmlUtils.ReadDoubleValue(instance.Element("Length"), 0);
var lyrics = XmlUtils.ReadStringValue(instance.Element("Text"), "");
var clip = new KaraokeClip
{
Id = IdGenerator.Instance.Generate(),
StartBeat = tape.TimeInBeats(position, offset),
BeatDuration = tape.TimeInBeats(position, offset + length) - tape.TimeInBeats(position, offset),
ContentType = KaraokeClip.ContentType_Enum.KaraokeContent_LyricsOnly,
MidiNote = 0,
TrackId = 0,
IsEndOfLine = true,
Lyrics = lyrics.Replace("_", " ")
};
lyricsClips.Add(clip);
}
}
var layerNamedKaraoke = layers.FirstOrDefault(x => x.Attribute("name").Value == "Karaoke");
var layerNamedLyricsKaraoke = layers.FirstOrDefault(x => x.Attribute("name").Value == "LyricsKaraoke");
var karaokeLayer = layerNamedLyricsKaraoke ?? layerNamedKaraoke;
if (karaokeLayer != null)
{
foreach (var instance in karaokeLayer.Descendants("instance"))
{
var previousClip = tape.Clips.LastOrDefault();
int position = Int32.Parse(instance.Attribute("position").Value)-1;
var offset = (float)XmlUtils.ReadDoubleValue(instance.Element("Offset"), 0);
var length = (float)XmlUtils.ReadDoubleValue(instance.Element("Length"), 0);
var lyrics = XmlUtils.ReadStringValue(instance.Element("Text"), "");
var clip = new KaraokeClip
{
Id = IdGenerator.Instance.Generate(),
StartBeat = tape.TimeInBeats(position,offset),
BeatDuration = tape.TimeInBeats(position, offset + length) - tape.TimeInBeats(position, offset),
ContentType = KaraokeClip.ContentType_Enum.KaraokeContent_LyricsOnly,
MidiNote = 0,
TrackId = 0,
Lyrics = lyrics.Replace("_", " ")
};
var currentLyricsLine = lyricsClips.FirstOrDefault(x => x.StartBeat <= clip.StartBeat && x.StopBeat >= clip.StopBeat);
clip.IsEndOfLine = currentLyricsLine != null && currentLyricsLine.Lyrics.EndsWith(clip.Lyrics, StringComparison.OrdinalIgnoreCase);
tape.Clips.Add(clip);
}
}
else
{
tape.Clips.AddRange(lyricsClips);
}
}
private void UpdateLyricsFile(Tape tape)
{
if (!System.IO.File.Exists(LyricsFile) || System.IO.File.ReadAllText(LyricsFile).StartsWith("Lorem ipsum") || string.IsNullOrEmpty(System.IO.File.ReadAllText(LyricsFile)))
{
string lyrics = "";
foreach (var karaokeClip in tape.Clips.OfType<KaraokeClip>())
{
lyrics += karaokeClip.Lyrics;
if (karaokeClip.IsEndOfLine)
{
lyrics += Environment.NewLine;
}
}
if (File.Exists(LyricsFile))
{
File.SetAttributes(LyricsFile, FileAttributes.Normal);
}
System.IO.File.WriteAllText(LyricsFile, lyrics);
}
}
}
}