diff --git a/BOXVR Playlist Manager/App.config b/BOXVR Playlist Manager/App.config
index eca0953..ea90831 100644
--- a/BOXVR Playlist Manager/App.config
+++ b/BOXVR Playlist Manager/App.config
@@ -1,30 +1,30 @@
-
+
-
-
-
+
+
+
-
+
-
+
-
+
-
+
-
+
-
\ No newline at end of file
+
diff --git a/BOXVR Playlist Manager/BOXVR Playlist Manager.csproj b/BOXVR Playlist Manager/BOXVR Playlist Manager.csproj
index 7ecab2a..280cd3a 100644
--- a/BOXVR Playlist Manager/BOXVR Playlist Manager.csproj
+++ b/BOXVR Playlist Manager/BOXVR Playlist Manager.csproj
@@ -8,12 +8,13 @@
WinExe
BoxVR_Playlist_Manager
BoxVR Playlist Manager
- v4.7.1
+ v4.7.2
512
{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
4
true
true
+
AnyCPU
@@ -51,6 +52,12 @@
..\packages\z440.atl.core.2.5.0\lib\net30\ATL.dll
+
+ ..\..\SpotiSharp\bin\Debug\netcoreapp3.1\HtmlAgilityPack.dll
+
+
+ ..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll
+
..\packages\NLog.4.5.10\lib\net45\NLog.dll
@@ -72,20 +79,77 @@
4.0
+
+ ..\packages\Unity3D.UnityEngine.2018.3.5.1\lib\UnityEngine.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
MSBuild:Compile
Designer
-
+
SettingsWindow.xaml
@@ -131,9 +195,6 @@
PreserveNewest
-
- Always
-
Designer
diff --git a/BOXVR Playlist Manager/Extensions.cs b/BOXVR Playlist Manager/Extensions.cs
index b98bfa1..062ac5a 100644
--- a/BOXVR Playlist Manager/Extensions.cs
+++ b/BOXVR Playlist Manager/Extensions.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Interop;
@@ -17,6 +18,15 @@ public static System.Windows.Forms.IWin32Window GetIWin32Window(this System.Wind
return win;
}
+ public static string Md5Hash(this string text)
+ {
+ using(MD5 md5 = MD5.Create())
+ {
+ var hash = md5.ComputeHash(Encoding.UTF8.GetBytes(text));
+ return Encoding.UTF8.GetString(hash);
+ }
+ }
+
private class OldWindow : System.Windows.Forms.IWin32Window
{
private readonly IntPtr _handle;
diff --git a/BOXVR Playlist Manager/FitXr/BeatStructure/Bar.cs b/BOXVR Playlist Manager/FitXr/BeatStructure/Bar.cs
new file mode 100644
index 0000000..7232315
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/BeatStructure/Bar.cs
@@ -0,0 +1,26 @@
+using Newtonsoft.Json;
+
+namespace BoxVR_Playlist_Manager.FitXr.BeatStructure
+{
+ public class Bar
+ {
+ [JsonProperty("_avgEnergy")]
+ public float _avgEnergy;
+ [JsonProperty("_duration")]
+ public float _duration;
+ [JsonProperty("_startTime")]
+ public float _startTime;
+ [JsonProperty("_beatIndex")]
+ public int _beatIndex;
+
+ public float Bpm => Bar.DurationToBpm(this._duration);
+
+ public float EndTime => this._startTime + this._duration;
+
+ public static float DurationToBpm(float duration) => 240f / duration;
+
+ public static float BpmToDuration(float bpm) => 240f / bpm;
+
+ public override string ToString() => "Start:" + (object)this._startTime + " Length:" + (object)this._duration;
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/BeatStructure/BarList.cs b/BOXVR Playlist Manager/FitXr/BeatStructure/BarList.cs
new file mode 100644
index 0000000..df17d35
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/BeatStructure/BarList.cs
@@ -0,0 +1,162 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace BoxVR_Playlist_Manager.FitXr.BeatStructure
+{
+ public class BarList
+ {
+ public List _bars;
+
+ protected float CalcMedianLength()
+ {
+ List barList = new List((IEnumerable)this._bars);
+ barList.Sort((Comparison)((x, y) => (int)Mathf.Sign(x._duration - y._duration)));
+ return barList[barList.Count / 2]._duration;
+ }
+
+ public void HalfSpeed()
+ {
+ for(int index = 0; index < this._bars.Count - 1; ++index)
+ {
+ Bar bar1 = this._bars[index];
+ Bar bar2 = this._bars[index + 1];
+ bar1._duration += bar2._duration;
+ bar1._avgEnergy = (float)(((double)bar1._avgEnergy + (double)bar2._avgEnergy) / 2.0);
+ this._bars.RemoveAt(index + 1);
+ }
+ }
+
+ public void DoubleSpeed(BeatList beats)
+ {
+ for(int index = 0; index < this._bars.Count; index += 2)
+ {
+ Bar bar1 = this._bars[index];
+ Bar bar2 = new Bar();
+ this._bars.Insert(index + 1, bar2);
+ float num = bar1._duration / 2f;
+ bar1._duration = num;
+ bar2._duration = num;
+ bar2._startTime = bar1._startTime + num;
+ }
+ }
+
+ public void UpdateEnergies(List beats)
+ {
+ for(int index1 = 0; index1 < this._bars.Count; ++index1)
+ {
+ Bar bar = this._bars[index1];
+ for(int index2 = 0; index2 <= 3; ++index2)
+ {
+ int index3 = bar._beatIndex + index2;
+ if(index3 < beats.Count && beats[index3]._beatInBar == index2 + 1)
+ bar._avgEnergy += beats[index3]._magnitude;
+ }
+ bar._avgEnergy /= 4f;
+ }
+ }
+
+ public void FillEmptyRegions(float deltaBpm, float songLength)
+ {
+ float duration1 = this.CalcMedianLength();
+ while(true)
+ {
+ float num = this._bars[0]._startTime - duration1;
+ if((double)num >= 0.0)
+ this._bars.Insert(0, new Bar()
+ {
+ _duration = duration1,
+ _startTime = num
+ });
+ else
+ break;
+ }
+ float duration2 = Bar.BpmToDuration(Bar.DurationToBpm(duration1) + deltaBpm);
+ for(int index = 0; index < this._bars.Count - 1; ++index)
+ {
+ double startTime = (double)this._bars[index + 1]._startTime;
+ float endTime = this._bars[index].EndTime;
+ double num1 = (double)endTime;
+ float num2 = (float)(startTime - num1);
+ if((double)num2 >= (double)duration1)
+ this._bars.Insert(index + 1, new Bar()
+ {
+ _duration = duration1,
+ _startTime = endTime
+ });
+ else if((double)num2 >= (double)duration2)
+ this._bars.Insert(index + 1, new Bar()
+ {
+ _duration = num2,
+ _startTime = endTime
+ });
+ else if((double)num2 > 0.0)
+ this._bars[index]._duration += num2;
+ }
+ while(true)
+ {
+ Bar bar = this._bars[this._bars.Count - 1];
+ if((double)bar.EndTime + (double)duration1 <= (double)songLength)
+ this._bars.Add(new Bar()
+ {
+ _startTime = bar.EndTime,
+ _duration = duration1
+ });
+ else
+ break;
+ }
+ }
+
+ public void RemoveDeviants(float bpmDelta)
+ {
+ float num = 240f / this.CalcMedianLength();
+ int index = 0;
+ while(index < this._bars.Count)
+ {
+ Bar bar = this._bars[index];
+ if((double)Mathf.Abs(bar.Bpm - num) > (double)bpmDelta)
+ this._bars.Remove(bar);
+ else
+ ++index;
+ }
+ }
+
+ public void CreateFromBeats(List beats)
+ {
+ this._bars = new List();
+ Bar bar = (Bar)null;
+ for(int index = 0; index < beats.Count; ++index)
+ {
+ BeatInfo beat = beats[index];
+ if(beat._beatInBar == 1)
+ {
+ bar = new Bar()
+ {
+ _startTime = beat._triggerTime,
+ _beatIndex = index
+ };
+ this._bars.Add(bar);
+ }
+ if(bar != null && beat._beatInBar == 4)
+ bar._duration = beat._triggerTime + beat._beatLength - bar._startTime;
+ }
+ this.UpdateEnergies(beats);
+ }
+
+ public int FindClosestBar(float time, List beats)
+ {
+ float num1 = float.MaxValue;
+ int num2 = -1;
+ for(int index = 0; index < this._bars.Count; ++index)
+ {
+ float num3 = Mathf.Abs(this._bars[index]._startTime - time);
+ if((double)num3 < (double)num1)
+ {
+ num2 = index;
+ num1 = num3;
+ }
+ }
+ return num2;
+ }
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/BeatStructure/Beat.cs b/BOXVR Playlist Manager/FitXr/BeatStructure/Beat.cs
new file mode 100644
index 0000000..59d1f51
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/BeatStructure/Beat.cs
@@ -0,0 +1,16 @@
+namespace BoxVR_Playlist_Manager.FitXr.BeatStructure
+{
+ public class Beat
+ {
+ public float length;
+ public float bpm;
+ public int index;
+
+ public Beat(float length, float bpm, int index)
+ {
+ this.length = length;
+ this.bpm = bpm;
+ this.index = index;
+ }
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/BeatStructure/BeatInfo.cs b/BOXVR Playlist Manager/FitXr/BeatStructure/BeatInfo.cs
new file mode 100644
index 0000000..8a2a9b4
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/BeatStructure/BeatInfo.cs
@@ -0,0 +1,45 @@
+namespace BoxVR_Playlist_Manager.FitXr.BeatStructure
+{
+ public class BeatInfo
+ {
+ public int _index;
+ public int _beatInBar;
+ public float _magnitude;
+ public float _triggerTime;
+ public float _beatLength;
+ public float _bpm;
+ public bool _isLastBeat;
+ public Segment _segment;
+
+ public BeatInfo(
+ int index,
+ float triggerTime,
+ float mag,
+ float beatLength,
+ float bpm,
+ int beatInBar,
+ bool isLastBeat)
+ {
+ this._beatInBar = beatInBar;
+ this._index = index;
+ this._triggerTime = triggerTime;
+ this._magnitude = mag;
+ this._beatLength = beatLength;
+ this._bpm = bpm;
+ this._isLastBeat = isLastBeat;
+ }
+
+ public BeatInfo(int index, float triggerTime, float mag, Beat beat, bool isLastBeat)
+ : this(index, triggerTime, mag, beat.length, beat.bpm, -1, isLastBeat)
+ {
+ }
+
+ public override string ToString()
+ {
+ string str = string.Format("{2,3} [{0,6:F2} s][{1,6} bpm][{3, 6:F2} magnitude]", (object)this._triggerTime, (object)this._bpm, (object)this._index, (object)this._magnitude);
+ if(this._segment != null)
+ str += this._segment.ToString();
+ return str + "\n";
+ }
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/BeatStructure/BeatList.cs b/BOXVR Playlist Manager/FitXr/BeatStructure/BeatList.cs
new file mode 100644
index 0000000..a2aba49
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/BeatStructure/BeatList.cs
@@ -0,0 +1,100 @@
+using System;
+using System.Collections.Generic;
+using BoxVR_Playlist_Manager.FitXr.Tools;
+using UnityEngine;
+
+namespace BoxVR_Playlist_Manager.FitXr.BeatStructure
+{
+ public class BeatList
+ {
+ public List _beats;
+ public float AverageBpm;
+
+ private float CalcMedianBpm(List beatList)
+ {
+ List beatInfoList = new List((IEnumerable)beatList);
+ beatInfoList.Sort((Comparison)((x, y) => (int)Mathf.Sign(x._bpm - y._bpm)));
+ return beatInfoList[beatInfoList.Count / 2]._bpm;
+ }
+
+ public void CreateFromBars(BarList barList)
+ {
+ this._beats = new List();
+ int index1 = 0;
+ for(int index2 = 0; index2 < barList._bars.Count; ++index2)
+ {
+ Bar bar = barList._bars[index2];
+ bar._beatIndex = index1;
+ float startTime = bar._startTime;
+ float num = bar._duration / 4f;
+ for(int beatInBar = 1; beatInBar < 5; ++beatInBar)
+ {
+ this._beats.Add(new BeatInfo(index1, startTime, -1f, -1f, -1f, beatInBar, false));
+ ++index1;
+ startTime += num;
+ }
+ }
+ for(int index2 = 0; index2 < this._beats.Count; ++index2)
+ {
+ this._beats[index2]._beatLength = index2 >= this._beats.Count - 1 ? this._beats[index2 - 1]._beatLength : this._beats[index2 + 1]._triggerTime - this._beats[index2]._triggerTime;
+ this._beats[index2]._bpm = 60f / this._beats[index2]._beatLength;
+ }
+ this.AverageBpm = this.CalcMedianBpm(this._beats);
+ for(int index2 = 0; index2 < this._beats.Count; ++index2)
+ this._beats[index2]._bpm = this.AverageBpm;
+ }
+
+ public void UpdateEnergiesFromLines(string[] lines)
+ {
+ int index1 = 0;
+ int index2 = 0;
+ this._beats[0]._magnitude = 0.0f;
+ do
+ {
+ float val1 = -1f;
+ float val2 = -1f;
+ Format.FloatsFromCsvLine(lines[index1], out val1, out val2);
+ while((double)val1 >= (double)this._beats[index2 + 1]._triggerTime)
+ {
+ ++index2;
+ this._beats[index2]._magnitude = 0.0f;
+ if(index2 == this._beats.Count - 1)
+ break;
+ }
+ this._beats[index2]._magnitude += val2;
+ ++index1;
+ }
+ while(index1 != lines.Length && index2 != this._beats.Count - 1);
+ }
+
+ public void CreateFromLines(string[] lines)
+ {
+ this._beats = new List();
+ int index1 = 0;
+ for(int index2 = 0; index2 < lines.Length; ++index2)
+ {
+ List stringList = new List((IEnumerable)lines[index2].Split('\t'));
+ float triggerTime = float.Parse(stringList[0]);
+ int beatInBar = int.Parse(stringList[1]);
+ this._beats.Add(new BeatInfo(index1, triggerTime, -1f, -1f, -1f, beatInBar, false));
+ ++index1;
+ }
+ for(int index2 = 0; index2 < this._beats.Count - 1; ++index2)
+ {
+ this._beats[index2]._beatLength = this._beats[index2 + 1]._triggerTime - this._beats[index2]._triggerTime;
+ this._beats[index2]._bpm = 60f / this._beats[index2]._beatLength;
+ }
+ if(this._beats.Count > 0)
+ {
+ this.AverageBpm = this.CalcMedianBpm(this._beats);
+ }
+ else
+ {
+ App.logger.Debug("MadMom failure");
+ this.AverageBpm = 120f;
+ }
+ for(int index2 = 0; index2 < this._beats.Count; ++index2)
+ this._beats[index2]._bpm = this.AverageBpm;
+ }
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/BeatStructure/BeatStructureBase.cs b/BOXVR Playlist Manager/FitXr/BeatStructure/BeatStructureBase.cs
new file mode 100644
index 0000000..3eb6fc4
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/BeatStructure/BeatStructureBase.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using Newtonsoft.Json;
+
+namespace BoxVR_Playlist_Manager.FitXr.BeatStructure
+{
+ public class BeatStructureBase
+ {
+
+ public event EventHandler _buildCompleteEvent;
+
+ protected bool _isBuilt;
+
+ public virtual List Beats => (List)null;
+
+ [JsonProperty("AverageBpm")]
+ public float AverageBpm { get; protected set; }
+
+ [JsonProperty("_isBuilt")]
+ public bool IsBuilt => this._isBuilt;
+
+ public virtual void Build()
+ {
+ }
+
+ public virtual BeatInfo GetBeatInInterval(float fromTime, float toTime)
+ {
+ foreach(BeatInfo beat in this.Beats)
+ {
+ if((double)beat._triggerTime >= (double)fromTime && (double)beat._triggerTime < (double)toTime)
+ return beat;
+ }
+ return (BeatInfo)null;
+ }
+
+ protected float CalcAverageBpm(List beatList)
+ {
+ float num = 0.0f;
+ foreach(BeatInfo beat in this.Beats)
+ num += beat._bpm;
+ return num / (float)this.Beats.Count;
+ }
+
+ protected void RaiseBuildCompleteEvent()
+ {
+ _buildCompleteEvent?.Invoke(this, EventArgs.Empty);
+ }
+
+ public override string ToString()
+ {
+ string str = "";
+ foreach(BeatInfo beat in this.Beats)
+ str += beat.ToString();
+ return str;
+ }
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/BeatStructure/BeatStructureMadmom.cs b/BOXVR Playlist Manager/FitXr/BeatStructure/BeatStructureMadmom.cs
new file mode 100644
index 0000000..77bb05c
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/BeatStructure/BeatStructureMadmom.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+
+namespace BoxVR_Playlist_Manager.FitXr.BeatStructure
+{
+ public class BeatStructureMadmom : BeatStructureBase
+ {
+ [JsonProperty("_barList")]
+ public BarList _barList;
+ [JsonProperty("_segmentList")]
+ public SegmentList _segmentList;
+ [JsonProperty("")]
+ public int _dataVersion = BeatStructureMadmom.DATA_VERSION;
+ public static int DATA_VERSION = 3;
+ [JsonProperty("_beatList")]
+ public BeatList _beatList;
+ private const float MAX_SPEED_BPM = 190f;
+ private const float MIN_SPEED_BPM = 90f;
+ private UserSongClip _userSong;
+ private MadmomProcess _madmomProcess;
+ private VampProcess _vampProcessSegments;
+ private VampProcess _vampProcessEnergies;
+
+ public override List Beats => this._beatList._beats;
+
+ public BeatStructureMadmom()
+ {
+ }
+
+ public BeatStructureMadmom(
+ UserSongClip userSong,
+ MadmomProcess madmomProcess,
+ VampProcess vampProcessSegments,
+ VampProcess vampProcessEnergies)
+ {
+ this._madmomProcess = madmomProcess;
+ this._userSong = userSong;
+ this._vampProcessSegments = vampProcessSegments;
+ this._vampProcessEnergies = vampProcessEnergies;
+ this._isBuilt = false;
+ }
+
+ public override void Build() => this._madmomProcess.Apply(this._userSong.wavPath, OnBeatsTracked);
+
+ private void OnBeatsTracked()
+ {
+ this._beatList = new BeatList();
+ this._beatList.CreateFromLines(this._madmomProcess._resultEntries);
+ this._barList = new BarList();
+ this._barList.CreateFromBeats(this._beatList._beats);
+ this._barList.RemoveDeviants(2f);
+ this._barList.FillEmptyRegions(8f, this._userSong.trackData.duration);
+ if((double)this._barList._bars[0].Bpm > 190.0)
+ this._barList.HalfSpeed();
+ else if((double)this._barList._bars[0].Bpm < 90.0)
+ this._barList.DoubleSpeed(this._beatList);
+ this._beatList.CreateFromBars(this._barList);
+ this.AverageBpm = this._beatList.AverageBpm;
+ // ISSUE: method pointer
+ this._vampProcessEnergies.Apply("vamp:bbc-vamp-plugins:bbc-energy", this._userSong.wavPath, OnEnergiesTracked);
+ }
+
+ private void OnEnergiesTracked()
+ {
+ this._beatList.UpdateEnergiesFromLines(this._vampProcessEnergies._resultEntries);
+ this._barList.UpdateEnergies(this.Beats);
+ // ISSUE: method pointer
+ this._vampProcessSegments.Apply("vamp:qm-vamp-plugins:qm-segmenter", this._userSong.wavPath,OnSegmentsTracked);
+ }
+
+ private void OnSegmentsTracked()
+ {
+ this._segmentList = new SegmentList(this._vampProcessSegments._resultEntries, this.Beats, this._barList);
+ App.logger.Debug(this._segmentList.ToString());
+ List segments = this._segmentList._segments;
+ for(int index1 = 0; index1 < segments.Count; ++index1)
+ {
+ for(int index2 = 0; index2 < segments[index1]._numBeats; ++index2)
+ this.Beats[segments[index1]._startBeatIndex + index2]._segment = segments[index1];
+ }
+ this.OnBuildComplete(); }
+
+ private void OnBuildComplete()
+ {
+ this._isBuilt = true;
+ RaiseBuildCompleteEvent();
+ }
+ }
+ }
diff --git a/BOXVR Playlist Manager/FitXr/BeatStructure/FFmpegJob.cs b/BOXVR Playlist Manager/FitXr/BeatStructure/FFmpegJob.cs
new file mode 100644
index 0000000..97f8d4c
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/BeatStructure/FFmpegJob.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace BoxVR_Playlist_Manager.FitXr.BeatStructure
+{
+ public class FFmpegJob
+ {
+ public Action _onFinished;
+ public string _inputPath;
+ public string _outputPath;
+ public string _message;
+
+ public virtual string GetCommand() => "";
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/BeatStructure/FFmpegJobConvert.cs b/BOXVR Playlist Manager/FitXr/BeatStructure/FFmpegJobConvert.cs
new file mode 100644
index 0000000..4eeb967
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/BeatStructure/FFmpegJobConvert.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace BoxVR_Playlist_Manager.FitXr.BeatStructure
+{
+ public class FFmpegJobConvert : FFmpegJob
+ {
+ public FFmpegJobConvert(string inputPath, string outputPath, Action onFinished)
+ {
+ this._inputPath = inputPath;
+ this._outputPath = outputPath;
+ this._onFinished = onFinished;
+ }
+
+ public override string GetCommand() => string.Join(" ", new string[7]
+ {
+ "-y",
+ "-i",
+ "\"" + this._inputPath + "\"",
+ "-ar 44100 ",
+ "-acodec pcm_s16le ",
+ "-nostdin -loglevel error",
+ "\"" + this._outputPath + "\""
+ });
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/BeatStructure/FFmpegQueue.cs b/BOXVR Playlist Manager/FitXr/BeatStructure/FFmpegQueue.cs
new file mode 100644
index 0000000..e687949
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/BeatStructure/FFmpegQueue.cs
@@ -0,0 +1,70 @@
+using System.Collections;
+using System.Diagnostics;
+using System.IO;
+using System.Threading;
+using BoxVR_Playlist_Manager.FitXr.Enums;
+using BoxVR_Playlist_Manager.Helpers;
+
+namespace BoxVR_Playlist_Manager.FitXr.BeatStructure
+{
+ public class FFmpegQueue
+ {
+ public static FFmpegQueue instance = instance == null ? new FFmpegQueue() : instance;
+
+ public void Queue(FFmpegJob job) => RunFFMPEG(job);
+
+ public static string binaryPath => Path.Combine(Paths.StreamingAssetsPath, @"ffmpeg\bin\ffmpeg.exe");
+
+ private void RunFFMPEG(FFmpegJob job)
+ {
+ Directory.CreateDirectory(Paths.TrackDataFolder(LocationMode.PlayerData));
+ App.logger.Debug(("FFMPEG start: " + FFmpegQueue.binaryPath + " " + job.GetCommand()));
+ CancellationTokenSource doneCts = new CancellationTokenSource();
+ var done = doneCts.Token;
+ string exepath = FFmpegQueue.binaryPath;
+ string command = job.GetCommand();
+ new Thread((ThreadStart)(() =>
+ {
+ string output = "";
+ Process process = new Process();
+ process.StartInfo.RedirectStandardOutput = true;
+ process.StartInfo.RedirectStandardError = true;
+ process.StartInfo.UseShellExecute = false;
+ process.StartInfo.CreateNoWindow = true;
+ process.StartInfo.FileName = exepath;
+ process.StartInfo.Arguments = command;
+ process.OutputDataReceived += (DataReceivedEventHandler)((s, e) => output += e.Data);
+ process.ErrorDataReceived += (DataReceivedEventHandler)((s, e) => output += e.Data);
+ process.Start();
+ process.WaitForExit();
+ process.Close();
+ job._message = output;
+ doneCts.Cancel();
+ })).Start();
+ while(!done.IsCancellationRequested) { }
+ job._onFinished.Invoke(job);
+ App.logger.Debug("FFMEG done");
+ }
+
+ public bool ExtractDurationFromFFmpegOutput(string output, out int minutes, out int seconds)
+ {
+ minutes = -1;
+ seconds = -1;
+ string str1 = "Duration: ";
+ string str2 = "xx:xx:xx.xx";
+ int startIndex = output.IndexOf(str1) + str1.Length;
+ string[] strArray = output.Substring(startIndex, str2.Length).Split(':', '.');
+ if(strArray == null || strArray.Length != 4)
+ return false;
+ int[] numArray = new int[strArray.Length];
+ for(int index = 0; index < strArray.Length; ++index)
+ {
+ if(!int.TryParse(strArray[index], out numArray[index]))
+ return false;
+ }
+ minutes = numArray[0] * 60 + numArray[1];
+ seconds = numArray[2];
+ return true;
+ }
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/BeatStructure/MadmomProcess.cs b/BOXVR Playlist Manager/FitXr/BeatStructure/MadmomProcess.cs
new file mode 100644
index 0000000..7e68cf5
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/BeatStructure/MadmomProcess.cs
@@ -0,0 +1,100 @@
+
+using System;
+using System.Collections;
+using System.Diagnostics;
+using System.IO;
+using System.Threading;
+using BoxVR_Playlist_Manager.Helpers;
+
+namespace BoxVR_Playlist_Manager.FitXr.BeatStructure
+{
+ public class MadmomProcess
+ {
+ public static string madmonOutputPath;
+ public string[] _resultEntries;
+ public string _output;
+ private const bool SHOW_WINDOW = false;
+ private string processPath;
+ private string outputFileName;
+
+ private string BinaryPath => Path.Combine(Paths.StreamingAssetsPath, @"DBNDownBeatTracker\DBNDownBeatTracker.exe");
+
+ public MadmomProcess()
+ {
+ madmonOutputPath = Path.Combine(Paths.PersistentDataPath, "Temp");
+ this.processPath = this.BinaryPath;
+ if(Directory.Exists(MadmomProcess.madmonOutputPath))
+ {
+ return;
+ }
+ Directory.CreateDirectory(MadmomProcess.madmonOutputPath);
+ }
+
+ private string CreateCommandArgs(string musicPath, string outputPath) => string.Format("--beats_per_bar 4 single \"{0}\" -o \"{1}\" -j 1", (object)musicPath, (object)outputPath);
+
+ public void Apply(string musicPath, Action successAction)
+ {
+ ProcessMadmom(musicPath, successAction);
+ }
+
+ private void ProcessMadmom(string musicPath, Action successAction)
+ {
+ outputFileName = Path.GetFileNameWithoutExtension(musicPath) + ".madmom.txt";
+ string resultOutPath = Path.Combine(MadmomProcess.madmonOutputPath, Path.GetFileNameWithoutExtension(musicPath) + ".madmom.txt");
+ string ffmpegPath = Path.GetDirectoryName(FFmpegQueue.binaryPath);
+ string arguments = this.CreateCommandArgs(musicPath, resultOutPath);
+ string path = MadmomProcess.madmonOutputPath + this.outputFileName;
+ File.Delete(resultOutPath);
+ File.Delete(path);
+ bool isProcessComplete = false;
+ App.logger.Debug(arguments);
+ Process process = (Process)null;
+ new Thread((ThreadStart)(() =>
+ {
+ App.logger.Debug("Madmom started");
+ process = new Process()
+ {
+ StartInfo = {
+ FileName = this.processPath,
+ UseShellExecute = false,
+ EnvironmentVariables = {
+ {
+ "PROGRESS_PATH",
+ MadmomProcess.madmonOutputPath + this.outputFileName
+ }
+ }
+ }
+ };
+ process.StartInfo.EnvironmentVariables["PATH"] = process.StartInfo.EnvironmentVariables["PATH"] + ";" + ffmpegPath;
+ process.StartInfo.CreateNoWindow = true;
+ process.StartInfo.Arguments = arguments;
+ isProcessComplete = false;
+ process.Start();
+ process.PriorityClass = ProcessPriorityClass.BelowNormal;
+ process.WaitForExit();
+ isProcessComplete = true;
+ })).Start();
+ App.logger.Debug("Waiting for madmom");
+ while(!isProcessComplete){ }
+ while(!process.HasExited){ }
+ int exitCode = process.ExitCode;
+ process.Close();
+ App.logger.Debug("Madmom finished : error code= " + (object)exitCode);
+ this._resultEntries = new string[0];
+ if(File.Exists(resultOutPath))
+ {
+ this._resultEntries = File.ReadAllLines(resultOutPath);
+ }
+
+ if(exitCode == -1 || this._resultEntries.Length == 0)
+ {
+ throw new System.Exception("Process error");
+ }
+ else
+ {
+ successAction.Invoke();
+ }
+ File.Delete(resultOutPath);
+ }
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/BeatStructure/PlaylistManager.cs b/BOXVR Playlist Manager/FitXr/BeatStructure/PlaylistManager.cs
new file mode 100644
index 0000000..bb5b7c6
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/BeatStructure/PlaylistManager.cs
@@ -0,0 +1,330 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Collections;
+using System.IO;
+using BoxVR_Playlist_Manager.FitXr.Models;
+using BoxVR_Playlist_Manager.Helpers;
+using BoxVR_Playlist_Manager.FitXr.Enums;
+using Newtonsoft.Json;
+using BoxVR_Playlist_Manager.FitXr.Utility;
+
+namespace BoxVR_Playlist_Manager.FitXr.BeatStructure
+{
+ public class PlaylistManager
+ {
+ public static int MAX_NUM_ENTRIES = 20;
+ public static PlaylistManager instance = instance == null ? new PlaylistManager() : instance;
+ private string playlistsPath;
+ public List workoutPlaylists;
+ private List playlistQueue;
+ private bool isProcessing;
+
+ public event PlaylistManager.OnTrackAdditionComplete onTrackAdditionComplete;
+
+ public PlaylistManager()
+ {
+ Directory.CreateDirectory(Paths.RootDataFolder(LocationMode.PlayerData));
+ Directory.CreateDirectory(Paths.WorkoutDefinitionFolder(LocationMode.PlayerData, Game.BoxVR));
+ Directory.CreateDirectory(Paths.TrackDataFolder(LocationMode.PlayerData));
+ Directory.CreateDirectory(Paths.DefinitionsFolder(LocationMode.PlayerData));
+ Directory.CreateDirectory(Paths.ImgDataFolder(LocationMode.PlayerData));
+ this.playlistsPath = Paths.WorkoutDefinitionFolder(LocationMode.PlayerData, Game.BoxVR);
+ if(Directory.Exists(this.playlistsPath))
+ return;
+ Directory.CreateDirectory(this.playlistsPath);
+ }
+
+
+ public List GetAllWorkoutPlaylists() => this.workoutPlaylists;
+
+ public List ReturnAllPlaylistsFromDisk() => !Directory.Exists(this.playlistsPath) ? new List() : new List((IEnumerable)Tools.IO.GetVisibleFiles(this.playlistsPath, "*.play"));
+
+ public void LoadWorkoutPlaylists()
+ {
+ this.workoutPlaylists = this.LoadAllWorkoutPlaylistsFromDisk();
+ LoadPlaylistTrackDefinition();
+ foreach(var playlist in workoutPlaylists)
+ {
+ playlist.CalcTotalLength();
+ }
+ }
+
+ private void LoadPlaylistTrackDefinition()
+ {
+ while(TrackDataManager.instance == null) { }
+ while(!TrackDataManager.instance.trackListLoaded) { }
+ for(int index1 = 0; index1 < this.workoutPlaylists.Count; ++index1)
+ {
+ for(int index2 = 0; index2 < this.workoutPlaylists[index1].songs.Count; ++index2)
+ {
+ this.workoutPlaylists[index1].songs[index2].trackDefinition = new TrackDefinition();
+ this.workoutPlaylists[index1].songs[index2].trackDefinition.LoadTrackDefinition(this.workoutPlaylists[index1].songs[index2].trackDataName, LocationMode.PlayerData);
+ }
+ }
+ }
+
+ public List LoadAllWorkoutPlaylistsFromDisk()
+ {
+ List workoutPlaylistList = new List();
+ if(Directory.Exists(this.playlistsPath))
+ {
+ foreach(string visibleFile in Tools.IO.GetVisibleFiles(this.playlistsPath, "*.workoutplaylist.txt"))
+ {
+ WorkoutPlaylist workoutPlaylist = new WorkoutPlaylist();
+ string jsonString = File.ReadAllText(visibleFile);
+ workoutPlaylist.LoadFromJSON(jsonString);
+ workoutPlaylistList.Add(workoutPlaylist);
+ }
+ }
+ return workoutPlaylistList;
+ }
+
+ public void Saveplaylists()
+ {
+
+ for(int index = 0; index < this.workoutPlaylists.Count; ++index)
+ this.ExportPlaylistJson(this.workoutPlaylists[index]);
+ }
+
+ public void ExportPlaylistJson(WorkoutPlaylist playlist)
+ {
+ playlist.CalcTotalLength();
+ if(playlist.definition.originalName != playlist.definition.workoutName)
+ {
+ DeleteJsonFile(playlist.definition.originalName);
+ }
+ File.WriteAllText(Path.Combine(this.playlistsPath, playlist.definition.workoutName + ".workoutplaylist.txt"), JsonConvert.SerializeObject(playlist));
+ }
+
+ public string PlaylistJsonFromName(string playlistName)
+ {
+ string str1 = "";
+ string str2 = Path.Combine(this.playlistsPath, playlistName + ".play");
+ if((!File.Exists(str2) ? 0 : (new FileInfo(str2).Length > 0L ? 1 : 0)) != 0)
+ str1 = File.ReadAllText(str2);
+ return str1;
+ }
+
+ public void DeletePlaylist(string playlistName)
+ {
+ WorkoutPlaylist playlistByName = this.GetPlaylistByName(playlistName);
+ if(playlistByName == null)
+ return;
+ this.DeletePlaylist(playlistByName);
+ }
+
+ public void DeletePlaylist(WorkoutPlaylist playlist)
+ {
+ this.DeleteJsonFile(playlist.definition.workoutName);
+ this.workoutPlaylists.Remove(playlist);
+ //this.Saveplaylists();
+ }
+
+ public List ReturnPlaylistTrackDefinitions(
+ WorkoutPlaylist playlist)
+ {
+ List trackDefinitionList = new List();
+ if(playlist == null || playlist.songs == null)
+ return (List)null;
+ for(int index = 0; index < playlist.songs.Count; ++index)
+ {
+ playlist.songs[index].trackDefinition = TrackDataManager.instance.GetTrackDefinition(playlist.songs[index].trackDataName);
+ trackDefinitionList.Add(playlist.songs[index].trackDefinition);
+ }
+ return trackDefinitionList;
+ }
+
+ public void PlaylistEditName(WorkoutPlaylist playlist, string newName)
+ {
+ string workoutName = playlist.definition.workoutName;
+ if(newName == workoutName)
+ return;
+ playlist.definition.workoutName = newName;
+ this.ExportPlaylistJson(playlist);
+ this.DeleteJsonFile(workoutName);
+ }
+
+ private void DeleteJsonFile(string workoutName) => File.Delete(Path.Combine(this.playlistsPath, workoutName + ".workoutplaylist.txt"));
+
+ public SongDefinition PlaylistAddEntry(WorkoutPlaylist playlist, string fileName, LocationMode location)
+ {
+ if(this.playlistQueue == null)
+ this.playlistQueue = new List();
+ PlaylistManager.PlaylistToAdd playlistToAdd = new PlaylistManager.PlaylistToAdd()
+ {
+ playlistToAdd = playlist,
+ songToAdd = new SongDefinition()
+ };
+ playlistToAdd.songToAdd.trackDefinition = new TrackDefinition();
+ playlistToAdd.originalFilePath = fileName;
+ playlistToAdd.location = location;
+ this.playlistQueue.Add(playlistToAdd);
+ if(this.isProcessing)
+ throw new Exception("Can't batch add yet");
+ this.PlaylistAddProcess();
+ return playlistToAdd.songToAdd;
+ }
+
+ public void PlaylistAddProcess()
+ {
+ this.isProcessing = true;
+ string originalFilePath = this.playlistQueue[0].originalFilePath;
+ LocationMode location = this.playlistQueue[0].location;
+ App.logger.Debug("Location mode = " + location.ToString());
+ var trackHash = MD5.MD5Sum(Path.GetFileNameWithoutExtension(originalFilePath));
+ if(this.playlistQueue[0].songToAdd.trackDefinition.LoadTrackDefinition(trackHash, location))
+ {
+ this.playlistQueue[0].songToAdd.trackDataName = this.playlistQueue[0].songToAdd.trackDefinition.trackId.trackId;
+ this.playlistQueue[0].playlistToAdd.AddSong(this.playlistQueue[0].songToAdd);
+ this.ExportPlaylistJson(this.playlistQueue[0].playlistToAdd);
+ this.WaitingOver();
+ }
+ else
+ {
+ this.playlistQueue[0].songToAdd.trackDefinition.trackData = new TrackData();
+ this.playlistQueue[0].songToAdd.trackDefinition.trackData.PopulateTrackDataFromAudioFile(originalFilePath, location, Game.BoxVR);
+ WaitForTrackData();
+ }
+ }
+
+ private void WaitForTrackData()
+ {
+ while(this.playlistQueue[0].songToAdd.trackDefinition.trackData.trackDataState == TrackDataState.Loading) { }
+ if(this.playlistQueue[0].songToAdd.trackDefinition.trackData.trackDataState == TrackDataState.Ready)
+ {
+ this.playlistQueue[0].songToAdd.trackDataName = this.playlistQueue[0].songToAdd.trackDefinition.trackData.trackId.trackId;
+ this.playlistQueue[0].songToAdd.trackDefinition.LoadTrackDefinition(playlistQueue[0].songToAdd.trackDataName, playlistQueue[0].location);
+ this.playlistQueue[0].playlistToAdd.AddSong(this.playlistQueue[0].songToAdd);
+ this.ExportPlaylistJson(this.playlistQueue[0].playlistToAdd);
+ }
+ else
+ App.logger.Debug("TrackData failed");
+ this.WaitingOver();
+ }
+
+ private void WaitingOver()
+ {
+ this.playlistQueue.RemoveAt(0);
+ this.isProcessing = false;
+ if(this.playlistQueue.Count > 0)
+ {
+ this.PlaylistAddProcess();
+ }
+ else
+ {
+ if(this.onTrackAdditionComplete == null)
+ return;
+ this.onTrackAdditionComplete();
+ this.onTrackAdditionComplete = (PlaylistManager.OnTrackAdditionComplete)null;
+ }
+ }
+
+ public int ParsingQueueSize() => this.playlistQueue == null ? 0 : this.playlistQueue.Count;
+
+ public void PlayistRemoveEntry(WorkoutPlaylist playlist, string filePath)
+ {
+ string withoutExtension = Path.GetFileNameWithoutExtension(filePath);
+ for(int index = 0; index < playlist.songs.Count; ++index)
+ {
+ if(playlist.songs[index].trackDataName == withoutExtension)
+ {
+ playlist.RemoveSong(index);
+ this.ExportPlaylistJson(playlist);
+ break;
+ }
+ }
+ }
+
+ public WorkoutPlaylist AddNewPlaylist(string name = "")
+ {
+ if(this.workoutPlaylists == null)
+ this.workoutPlaylists = new List();
+ if(name == "")
+ name = this.GeneratePlaylistName();
+ WorkoutPlaylist workoutPlaylist = new WorkoutPlaylist()
+ {
+ definition = new WorkoutInfo()
+ };
+ workoutPlaylist.definition.workoutName = name;
+ workoutPlaylist.definition.originalName = name;
+ workoutPlaylist.definition.workoutType = WorkoutType.BoxVR_Playlist;
+ this.workoutPlaylists.Add(workoutPlaylist);
+ return workoutPlaylist;
+ }
+
+ private string GeneratePlaylistName()
+ {
+ string str = "playlist";
+ string id = str;
+ int num = 1;
+ while(this.IsIdInPlaylists(id))
+ {
+ id = str + " " + num.ToString();
+ ++num;
+ }
+ return id;
+ }
+
+ private bool IsIdInPlaylists(string id)
+ {
+ foreach(WorkoutPlaylist workoutPlaylist in this.workoutPlaylists)
+ {
+ if(workoutPlaylist.definition.workoutName == id)
+ return true;
+ }
+ return false;
+ }
+
+ public WorkoutPlaylist GetPlaylistByName(string name)
+ {
+ if(this.workoutPlaylists != null)
+ {
+ for(int index = 0; index < this.workoutPlaylists.Count; ++index)
+ {
+ if(this.workoutPlaylists[index].definition.workoutName == name)
+ return this.workoutPlaylists[index];
+ }
+ }
+ return (WorkoutPlaylist)null;
+ }
+
+ public void ClearOrphanedFile(string playlistName)
+ {
+ File.Delete(Path.Combine(this.playlistsPath, playlistName + ".play"));
+ }
+
+ public void ShufflePlaylistTracks(WorkoutPlaylist playlist) => playlist.songs.Shuffle();
+
+ public List GetAllOldSaves()
+ {
+ string path = Path.Combine(Paths.PersistentDataPath, "Playlists");
+ return !Directory.Exists(path) ? new List() : new List((IEnumerable)Tools.IO.GetVisibleFiles(path, "*.playlist.txt"));
+ }
+
+ public List ReturnSongPathsFromOldSavePath(string SaveFilePath)
+ {
+ List stringList = new List();
+ foreach(string readAllLine in File.ReadAllLines(SaveFilePath))
+ {
+ if(File.Exists(readAllLine))
+ stringList.Add(readAllLine);
+ }
+ return stringList;
+ }
+
+ private void ConvertLegacyPlaylistsToGen3()
+ {
+ }
+
+ public class PlaylistToAdd
+ {
+ public WorkoutPlaylist playlistToAdd;
+ public SongDefinition songToAdd;
+ public string originalFilePath;
+ public LocationMode location;
+ }
+
+ public delegate void OnTrackAdditionComplete();
+ }
+}
\ No newline at end of file
diff --git a/BOXVR Playlist Manager/FitXr/BeatStructure/Segment.cs b/BOXVR Playlist Manager/FitXr/BeatStructure/Segment.cs
new file mode 100644
index 0000000..2ef7813
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/BeatStructure/Segment.cs
@@ -0,0 +1,42 @@
+using System.Text;
+
+namespace BoxVR_Playlist_Manager.FitXr.BeatStructure
+{
+ public class Segment
+ {
+ public float _startTime;
+ public int _startBeatIndex;
+ public float _length;
+ public int _numBeats;
+ public float _averageEnergy;
+ public int _index;
+ public Segment.EnergyLevel _energyLevel;
+
+ public Segment(float startTime, float length)
+ {
+ this._startTime = startTime;
+ this._length = length;
+ }
+
+ public float endTime => this._startTime + this._length;
+
+ public override string ToString()
+ {
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.Append("Segment:");
+ stringBuilder.AppendFormat("[{0, 6:F2} Start]", (object)this._startTime);
+ stringBuilder.AppendFormat("[{0, 6:F2} Energy]", (object)this._averageEnergy);
+ stringBuilder.AppendFormat("[{0, 6:F2} Range]", (object)this._energyLevel.ToString());
+ return stringBuilder.ToString();
+ }
+
+ public enum EnergyLevel
+ {
+ UltraLow,
+ Low,
+ Medium,
+ High,
+ Size,
+ }
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/BeatStructure/SegmentList.cs b/BOXVR Playlist Manager/FitXr/BeatStructure/SegmentList.cs
new file mode 100644
index 0000000..cc432b2
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/BeatStructure/SegmentList.cs
@@ -0,0 +1,137 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using UnityEngine;
+
+namespace BoxVR_Playlist_Manager.FitXr.BeatStructure
+{
+ public class SegmentList
+ {
+ public List _segments;
+
+ public SegmentList(string[] csvLines, List beats, BarList bars)
+ {
+ this._segments = new List();
+ foreach(string csvLine in csvLines)
+ {
+ List stringList = new List((IEnumerable)csvLine.Split(','));
+ int num = stringList.Count - 1;
+ if(num < 4)
+ App.logger.Error(("Not enough entries in line " + csvLine));
+ this._segments.Add(new Segment(float.Parse(stringList[num - 3]), float.Parse(stringList[num - 2])));
+ }
+ this.AlignSegmentsWithBeats(ref this._segments, beats, bars);
+ this.CalculateSegmentEnergies(ref this._segments, beats);
+ }
+
+ private void CalculateSegmentEnergies(ref List segments, List beats)
+ {
+ for(int index1 = 0; index1 < segments.Count; ++index1)
+ {
+ float num1 = 0.0f;
+ for(int index2 = 0; index2 < segments[index1]._numBeats; ++index2)
+ {
+ int index3 = segments[index1]._startBeatIndex + index2;
+ num1 += beats[index3]._magnitude;
+ }
+ float num2 = num1 / (float)segments[index1]._numBeats;
+ segments[index1]._averageEnergy = num2;
+ }
+ float[] numArray1 = new float[4]
+ {
+ 0.1f,
+ 0.3f,
+ 0.3f,
+ 0.3f
+ };
+ int[] numArray2 = new int[4];
+ List segmentList = new List((IEnumerable)segments);
+ segmentList.Sort((Comparison)((x, y) => (int)Mathf.Sign(x._averageEnergy - y._averageEnergy)));
+ int num3 = this._segments[this._segments.Count - 1]._startBeatIndex + this._segments[this._segments.Count - 1]._numBeats;
+ int num4 = 0;
+ for(int index = 0; index < 4; ++index)
+ {
+ if(index == 3)
+ {
+ numArray2[index] = num3 - num4;
+ }
+ else
+ {
+ numArray2[index] = (int)((double)num3 * (double)numArray1[index]);
+ num4 += numArray2[index];
+ }
+ }
+ int index4 = 0;
+ int[] numArray3 = new int[4] { 0, 0, 0, 0 };
+ for(int index1 = 0; index1 < segmentList.Count; ++index1)
+ {
+ segmentList[index1]._energyLevel = (Segment.EnergyLevel)index4;
+ numArray3[index4] += segmentList[index1]._numBeats;
+ if(numArray3[index4] > numArray2[index4])
+ {
+ ++index4;
+ numArray3[index4] = 0;
+ }
+ }
+ }
+
+ private int FindLargestNeighbourEnergyDelta(List bars, int centerBarIndex, int range)
+ {
+ float[] numArray = new float[2 * (range + 1)];
+ for(int index1 = -range - 1; index1 <= range; ++index1)
+ {
+ numArray[index1 + range + 1] = float.MinValue;
+ int index2 = index1 + centerBarIndex;
+ if(index2 < bars.Count && index2 >= 0)
+ numArray[index1 + range + 1] = bars[index2]._avgEnergy;
+ }
+ int length = 2 * range + 1;
+ float[] array = new float[length];
+ for(int index = 0; index < length; ++index)
+ array[index] = Mathf.Abs(numArray[index + 1] - numArray[index]);
+ float num = ((IEnumerable)array).Max();
+ return Array.IndexOf(array, num) - range;
+ }
+
+ private void AlignSegmentsWithBeats(
+ ref List segments,
+ List beats,
+ BarList bars)
+ {
+ for(int index1 = 0; index1 < segments.Count; ++index1)
+ {
+ int closestBar = bars.FindClosestBar(segments[index1]._startTime, beats);
+ int neighbourEnergyDelta = this.FindLargestNeighbourEnergyDelta(bars._bars, closestBar, 2);
+ int index2 = Mathf.Clamp(closestBar + neighbourEnergyDelta, 0, bars._bars.Count - 1);
+ int beatIndex = bars._bars[index2]._beatIndex;
+ segments[index1]._startBeatIndex = beatIndex;
+ }
+ List segmentList = new List();
+ List intList = new List();
+ foreach(Segment segment in this._segments)
+ {
+ if(intList.Contains(segment._startBeatIndex))
+ segmentList.Add(segment);
+ else
+ intList.Add(segment._startBeatIndex);
+ }
+ foreach(Segment segment in segmentList)
+ this._segments.Remove(segment);
+ for(int index = 0; index < segments.Count; ++index)
+ {
+ int num = (index < segments.Count - 1 ? segments[index + 1]._startBeatIndex : beats.Count) - segments[index]._startBeatIndex;
+ segments[index]._numBeats = num;
+ }
+ }
+
+ public override string ToString()
+ {
+ StringBuilder stringBuilder = new StringBuilder();
+ foreach(Segment segment in this._segments)
+ stringBuilder.AppendLine(segment.ToString());
+ return stringBuilder.ToString();
+ }
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/BeatStructure/SongParser.cs b/BOXVR Playlist Manager/FitXr/BeatStructure/SongParser.cs
new file mode 100644
index 0000000..f138860
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/BeatStructure/SongParser.cs
@@ -0,0 +1,85 @@
+using System;
+using BoxVR_Playlist_Manager.FitXr.Enums;
+using BoxVR_Playlist_Manager.FitXr.Models;
+using NLog;
+
+namespace BoxVR_Playlist_Manager.FitXr.BeatStructure
+{
+ public class SongParser
+ {
+ private static Logger _log = LogManager.GetLogger(nameof(SongParser));
+
+ public static SongParser instance = instance == null ? new SongParser() : instance;
+ public MadmomProcess _madmomProcess;
+ public VampProcess _vampProcessEnergies;
+ public VampProcess _vampProcessSegments;
+ public BeatStructureBase beatStructure;
+ private UserSongClip songClip;
+ private string passedPath;
+ private LocationMode passedlocationMode;
+ private bool busy;
+
+ public event SongParser.OnParsingComplete onParsingComplete;
+
+ public SongParser()
+ {
+ _madmomProcess = new MadmomProcess() { };
+ _vampProcessEnergies = new VampProcess();
+ _vampProcessSegments = new VampProcess();
+ }
+
+ ///
+ /// Entry to processing tasks
+ ///
+ /// Original audio track
+ /// File path to the .wav track
+ ///
+ ///
+ public bool CreateSongClipFromAudioClip(
+ ATL.Track audioTrack,
+ string wavPath,
+ LocationMode locationMode)
+ {
+ if(busy)
+ {
+ _log.Debug("Parsing a track already");
+ return false;
+ }
+ busy = true;
+ passedPath = wavPath;
+ passedlocationMode = locationMode;
+ WavLoaded(audioTrack);
+ return true;
+ }
+
+ private void WavLoaded(ATL.Track clip)
+ {
+ songClip = new UserSongClip(clip);
+
+ //Set filepath to .wav file
+ songClip.wavPath = passedPath;
+ ParseSong();
+ }
+
+ public void ParseSong()
+ {
+ this.beatStructure = (BeatStructureBase)new BeatStructureMadmom(this.songClip, this._madmomProcess, this._vampProcessEnergies, this._vampProcessSegments);
+ this.beatStructure._buildCompleteEvent += OnAnalysisComplete;
+ this.beatStructure.Build();
+ }
+
+ private void OnAnalysisComplete(object sender, EventArgs args)
+ {
+ this.busy = false;
+ this.beatStructure._buildCompleteEvent -= OnAnalysisComplete;
+ _log.Debug("Treack analysis complete");
+ TrackData trackData = TrackDataManager.instance.NewTrackData(songClip, (BeatStructureMadmom)this.beatStructure, this.passedlocationMode);
+ _log.Debug(trackData.originalTrackName);
+ onParsingComplete?.Invoke(trackData);
+ }
+
+ public delegate void OnParsingComplete(TrackData trackData);
+
+ public delegate void OnParsingFailed(string error);
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/BeatStructure/TrackDataManager.cs b/BOXVR Playlist Manager/FitXr/BeatStructure/TrackDataManager.cs
new file mode 100644
index 0000000..bca656a
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/BeatStructure/TrackDataManager.cs
@@ -0,0 +1,381 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Xml;
+using BoxVR_Playlist_Manager.FitXr.Enums;
+using BoxVR_Playlist_Manager.FitXr.Models;
+using BoxVR_Playlist_Manager.Helpers;
+using Newtonsoft.Json;
+using NLog;
+
+namespace BoxVR_Playlist_Manager.FitXr.BeatStructure
+{
+ class TrackDataManager
+ {
+ private static Logger _log = LogManager.GetLogger(nameof(TrackDataManager));
+ public static TrackDataManager instance = instance == null ? new TrackDataManager() : instance;
+ private ATL.Track audioTrack;
+ public string currentPath;
+ private Game currentGameType;
+ private List myWorkoutTrackDataList;
+ private List trackDefinitionsResources;
+ private List trackDefinitionsEditor;
+ private List trackDefinitionsPlayer;
+ private List trackDefinitionsDLC;
+ private string originalFileNameBackup;
+ private bool trackDataLoadComplete;
+ public bool trackListLoaded => this.trackDataLoadComplete;
+
+ public event TrackDataManager.OnTrackDataComplete onTrackDataComplete;
+
+ public TrackDataManager()
+ {
+ LoadTrackDefinitionLists();
+ }
+
+ public string GetLocationpathPath(
+ TrackId trackId,
+ LocationMode locationMode,
+ Game gameType)
+ {
+ string str = Paths.TrackDataFolder(locationMode);
+ switch(locationMode)
+ {
+ case LocationMode.PlayerData:
+ case LocationMode.Editor:
+ if(!Directory.Exists(Paths.RootDataFolder(locationMode)))
+ Directory.CreateDirectory(Paths.RootDataFolder(locationMode));
+ str = str + trackId.trackId + ".trackdata.txt";
+ break;
+ case LocationMode.Workouts:
+ case LocationMode.Downloadable:
+ case LocationMode.MyWorkout:
+ str = str + trackId.trackId + ".trackdata";
+ break;
+ }
+ return str;
+ }
+
+ public bool DoesTrackDataExist(string filePath, LocationMode locationMode, Game gameType)
+ {
+ TrackId trackId = new TrackId(filePath);
+ string locationpathPath = this.GetLocationpathPath(trackId, locationMode, gameType);
+
+ return System.IO.File.Exists(locationpathPath) && new FileInfo(locationpathPath).Length > 0L;
+ }
+
+ public bool DoesTrackDataExistByID(
+ TrackId trackId,
+ LocationMode locationMode,
+ Game gameType)
+ {
+ string locationpathPath = this.GetLocationpathPath(trackId, locationMode, gameType);
+ return System.IO.File.Exists(locationpathPath) && new FileInfo(locationpathPath).Length > 0L;
+ }
+
+ public bool DoesFileExist(TrackId trackId, string originalFilePath)
+ {
+ string path = Path.Combine(MadmomProcess.madmonOutputPath, trackId.trackId + ".madmom.txt");
+ bool flag1 = false;
+ if(System.IO.File.Exists(path))
+ flag1 = true;
+ string locationpathPath = this.GetLocationpathPath(trackId, LocationMode.PlayerData, Game.BoxVR);
+ bool flag2 = false;
+ if((!System.IO.File.Exists(locationpathPath) ? 0 : (new FileInfo(locationpathPath).Length > 0L ? 1 : 0)) != 0)
+ flag2 = true;
+ if(flag1 && !flag2)
+ {
+ this.LegacyToTrackData(trackId.trackId, originalFilePath);
+ this.ClearLegacyFiles(trackId.trackId);
+ return true;
+ }
+ return flag2;
+ }
+
+ public TrackData LegacyToTrackData(string trackMD5, string originalFilePath)
+ {
+ TrackData trackData = new TrackData();
+ var file = new ATL.Track(originalFilePath);
+ trackData.originalTrackName = file.Title;
+ trackData.trackId = new TrackId();
+ trackData.trackId.trackId = trackMD5;
+ trackData.duration = (float)file.Duration;
+ trackData.locationMode = LocationMode.PlayerData;
+ trackData.originalFilePath = originalFilePath;
+ if(string.IsNullOrEmpty(trackData.originalTrackName))
+ trackData.originalTrackName = Path.GetFileNameWithoutExtension(originalFilePath);
+ BeatStructureMadmom beatStructure = this.ReadBeatStructureFromFile(trackMD5);
+ trackData.bpm = beatStructure.AverageBpm;
+ trackData.beatStrucureJSON = this.BeatStructureToJSON(beatStructure);
+ this.SaveTrackData(trackData);
+ return trackData;
+ }
+
+ public TrackData NewTrackData(
+ UserSongClip clip,
+ BeatStructureMadmom bsm,
+ LocationMode locationMode)
+ {
+ TrackData trackData = new TrackData();
+ trackData.originalFilePath = clip.originalFilePath;
+ trackData.originalTrackName = clip.trackData.originalTrackName;
+ trackData.originalArtist = clip.trackData.originalArtist;
+ trackData.trackId = new TrackId(clip.originalFilePath);
+ trackData.bpm = bsm._beatList.AverageBpm;
+ trackData.beatStrucureJSON = BeatStructureToJSON(bsm);
+ trackData.duration = clip.trackData.duration;
+ trackData.locationMode = locationMode;
+ trackData.firstBeatOffset = bsm.Beats[0]._triggerTime;
+ SaveTrackData(trackData);
+ TrackDefinition trackDefinition = new TrackDefinition();
+ trackDefinition.locationMode = locationMode;
+ trackDefinition.trackId = trackData.trackId;
+ trackDefinition.tagLibTitle = trackData.originalTrackName;
+ trackDefinition.tagLibArtist = trackData.originalArtist;
+ trackDefinition.duration = trackData.duration;
+ trackDefinition.bpm = trackData.bpm;
+ trackDefinition.trackData = trackData;
+ trackDefinition.SaveTrackDefinition();
+ this.trackDefinitionsPlayer.Add(trackDefinition);
+ return trackData;
+ }
+
+ public void PopulateTrackDataFromAudioFile(string filePath, LocationMode locationMode, string originalFilePath, bool converted = false)
+ {
+ App.logger.Debug("Loading " + filePath);
+ string extension = Path.GetExtension(filePath);
+ if(extension == ".wav")
+ {
+ string filename = Path.GetFileNameWithoutExtension(filePath);
+ //Should never happen, but if we have a wav with a non md5 name, make it md5
+ if(!converted)
+ {
+ TrackId trackId = new TrackId(filePath);
+ filename = trackId.trackId;
+ }
+ string str = Paths.WavDataFolder(locationMode) + filename + ".wav";
+ if(!System.IO.File.Exists(str) && filePath != str)
+ {
+ System.IO.File.Copy(filePath, str);
+ }
+ LoadWav(str, locationMode, originalFilePath);
+ }
+ else
+ {
+ if(!(extension == ".mp3") && !(extension == ".m4a") && !(extension == ".ogg"))
+ {
+ return;
+ }
+
+ TrackId trackId = new TrackId(filePath);
+ string outputPath = Paths.WavDataFolder(locationMode) + trackId.trackId + ".wav";
+ //Convert to WAV
+ FFmpegQueue.instance.Queue((FFmpegJob)new FFmpegJobConvert(filePath, outputPath, OnConvertedToWav));
+ }
+ }
+
+ private void OnConvertedToWav(FFmpegJob job)
+ {
+ App.logger.Debug("FFMPEG complete: " + job._outputPath);
+ LocationMode locationMode = LocationMode.PlayerData;
+ this.originalFileNameBackup = Path.GetFileNameWithoutExtension(job._inputPath);
+ this.PopulateTrackDataFromAudioFile(job._outputPath, locationMode, job._inputPath, true);
+ }
+
+ public void WavLoaded(ATL.Track track, string wavPath, LocationMode locationMode)
+ {
+ _log.Debug("Wav Loaded");
+ this.audioTrack = track;
+ SongParser.instance.onParsingComplete += new SongParser.OnParsingComplete(OnParsingComplete);
+ SongParser.instance.CreateSongClipFromAudioClip(audioTrack, wavPath, locationMode);
+ }
+
+ private void OnParsingComplete(TrackData trackdata)
+ {
+ SongParser.instance.onParsingComplete -= new SongParser.OnParsingComplete(this.OnParsingComplete);
+ _log.Debug("Track data created");
+ onTrackDataComplete?.Invoke(trackdata);
+ }
+
+ public void ClearLegacyFiles(string trackMD5)
+ {
+ string path1 = Path.Combine(MadmomProcess.madmonOutputPath, trackMD5 + ".madmom.txt");
+ if(System.IO.File.Exists(path1))
+ System.IO.File.Delete(path1);
+ string path2 = Path.Combine(MadmomProcess.madmonOutputPath, trackMD5 + ".beatstructuremadmom.txt");
+ if(!System.IO.File.Exists(path2))
+ return;
+ System.IO.File.Delete(path2);
+ }
+
+ public string GetMadmomTextFromFilename(string trackMD5)
+ {
+ string str1 = "";
+ string str2 = Path.Combine(MadmomProcess.madmonOutputPath, trackMD5 + ".madmom.txt");
+ if((!System.IO.File.Exists(str2) ? 0 : (new FileInfo(str2).Length > 0L ? 1 : 0)) != 0)
+ str1 = System.IO.File.ReadAllText(str2);
+ return str1;
+ }
+
+ public string GetBeatStructureXMLFromFilename(string trackMD5)
+ {
+ string str1 = "";
+ string str2 = Path.Combine(MadmomProcess.madmonOutputPath, trackMD5 + ".beatstructuremadmom.txt");
+ if((!System.IO.File.Exists(str2) ? 0 : (new FileInfo(str2).Length > 0L ? 1 : 0)) != 0)
+ str1 = System.IO.File.ReadAllText(str2);
+ return str1;
+ }
+
+ public void SaveTrackData(TrackData trackData) => System.IO.File.WriteAllText(this.GetLocationpathPath(trackData.trackId, trackData.locationMode, Game.BoxVR), JsonConvert.SerializeObject(trackData));
+
+ public string TrackDataJsonFromFileName(TrackId trackId, LocationMode locationMode) => this.TrackDataJsonFromFilePath(this.GetLocationpathPath(trackId, locationMode, Game.BoxVR));
+
+ public TrackData TrackDataFromJson(string json) => JsonConvert.DeserializeObject(json);
+
+ public string TrackDataJsonFromFilePath(string filePath)
+ {
+ string str = "";
+ if((!System.IO.File.Exists(filePath) ? 0 : (new FileInfo(filePath).Length > 0L ? 1 : 0)) != 0)
+ str = System.IO.File.ReadAllText(filePath);
+ return str;
+ }
+ public string BeatStructureToJSON(BeatStructureMadmom beatStructure) => JsonConvert.SerializeObject(beatStructure);
+
+ public BeatStructureMadmom ReadBeatStructureFromFile(string trackMD5)
+ {
+ string inputUri = Path.Combine(MadmomProcess.madmonOutputPath, trackMD5 + ".beatstructuremadmom.txt");
+ using(XmlReader reader = XmlReader.Create(inputUri))
+ return (BeatStructureMadmom)new DataContractSerializer(typeof(BeatStructureMadmom)).ReadObject(reader);
+ }
+
+ public BeatStructureMadmom ReadBeatStructureFromJSON(string json) => JsonConvert.DeserializeObject(json);
+
+ public void LoadWav(string wavPath, LocationMode locationMode, string originalFileName)
+ {
+ var atlTrack = new ATL.Track(originalFileName);
+ WavLoaded(atlTrack, wavPath, locationMode);
+ }
+
+ public void LoadTrackDefinitionLists()
+ {
+ this.trackDataLoadComplete = false;
+ this.trackDefinitionsResources = new List();
+ this.trackDefinitionsPlayer = new List();
+ this.trackDefinitionsEditor = new List();
+ this.myWorkoutTrackDataList = new List();
+ this.trackDefinitionsDLC = new List();
+ string str = Paths.DefinitionsFolder(LocationMode.Workouts);
+ int count;
+ string path1 = Paths.DefinitionsFolder(LocationMode.PlayerData);
+ string[] fileNames;
+ if(Directory.Exists(path1))
+ {
+ fileNames = Directory.GetFiles(path1, "*.wdef.txt");
+ for(count = 0; count < fileNames.Length; ++count)
+ {
+ TrackDefinition trackDefinition = JsonConvert.DeserializeObject(System.IO.File.ReadAllText(fileNames[count]));
+ if(this.GetTrackDefinition(trackDefinition.trackId.trackId) == null)
+ {
+ trackDefinition.locationMode = LocationMode.PlayerData;
+ this.trackDefinitionsPlayer.Add(trackDefinition);
+ }
+ }
+ fileNames = (string[])null;
+ }
+ string path2 = Paths.DefinitionsFolder(LocationMode.Editor);
+ if(Directory.Exists(path2))
+ {
+ fileNames = Directory.GetFiles(path2, "*.wdef.txt");
+ for(count = 0; count < fileNames.Length; ++count)
+ {
+ TrackDefinition trackDefinition = JsonConvert.DeserializeObject(System.IO.File.ReadAllText(fileNames[count]));
+ if(this.GetTrackDefinition(trackDefinition.trackId.trackId) == null)
+ this.trackDefinitionsEditor.Add(trackDefinition);
+ trackDefinition.locationMode = LocationMode.Editor;
+ }
+ fileNames = (string[])null;
+ }
+ this.trackDataLoadComplete = true;
+ }
+
+
+ public List GetTrackDefinitionList()
+ {
+ List trackDefinitionList = new List();
+ trackDefinitionList.AddRange((IEnumerable)this.trackDefinitionsResources);
+ trackDefinitionList.AddRange((IEnumerable)this.trackDefinitionsPlayer);
+ trackDefinitionList.AddRange((IEnumerable)this.trackDefinitionsEditor);
+ trackDefinitionList.AddRange((IEnumerable)this.trackDefinitionsDLC);
+ return trackDefinitionList;
+ }
+
+ public void UpdateDLCTrackDefinitionList(List newList) => this.trackDefinitionsDLC = newList;
+
+ public TrackDefinition GetTrackDefinition(string trackId)
+ {
+ TrackDefinition trackDefinition = (TrackDefinition)null;
+ for(int index = 0; index < this.trackDefinitionsResources.Count; ++index)
+ {
+ if(this.trackDefinitionsResources[index].trackId.trackId == trackId)
+ {
+ trackDefinition = this.trackDefinitionsResources[index];
+ break;
+ }
+ }
+ if(trackDefinition == null)
+ {
+ for(int index = 0; index < this.trackDefinitionsPlayer.Count; ++index)
+ {
+ if(this.trackDefinitionsPlayer[index].trackId.trackId == trackId)
+ {
+ trackDefinition = this.trackDefinitionsPlayer[index];
+ break;
+ }
+ }
+ }
+ if(trackDefinition == null)
+ {
+ for(int index = 0; index < this.trackDefinitionsEditor.Count; ++index)
+ {
+ if(this.trackDefinitionsEditor[index].trackId.trackId == trackId)
+ {
+ trackDefinition = this.trackDefinitionsEditor[index];
+ break;
+ }
+ }
+ }
+ if(trackDefinition == null)
+ {
+ for(int index = 0; index < this.trackDefinitionsDLC.Count; ++index)
+ {
+ if(this.trackDefinitionsDLC[index].trackId.trackId == trackId)
+ {
+ trackDefinition = this.trackDefinitionsDLC[index];
+ break;
+ }
+ }
+ }
+ return trackDefinition;
+ }
+
+ public TrackData GetTrackDataByID(string trackDataId)
+ {
+ TrackDefinition trackDefinition = this.GetTrackDefinition(trackDataId);
+ if(trackDefinition == null)
+ return (TrackData)null;
+ trackDefinition.LoadTrackData();
+ return trackDefinition.trackData;
+ }
+
+ public delegate void OnWavLoaded(ATL.Track track, string filePath, LocationMode locationMode);
+
+ public delegate void OnWavLoadFailed(string error);
+
+ public delegate void OnTrackDataComplete(TrackData trackData);
+
+ public delegate void OnTrackDataFailed(string error);
+
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/BeatStructure/UserSongClip.cs b/BOXVR Playlist Manager/FitXr/BeatStructure/UserSongClip.cs
new file mode 100644
index 0000000..698afe3
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/BeatStructure/UserSongClip.cs
@@ -0,0 +1,36 @@
+using System;
+using System.IO;
+using BoxVR_Playlist_Manager.FitXr.Models;
+
+namespace BoxVR_Playlist_Manager.FitXr.BeatStructure
+{
+ public class UserSongClip
+ {
+ public Action onClipCreated;
+ public string wavPath;
+ public TrackId trackId;
+ public string originalFileName;
+ public string originalFilePath;
+ public TrackData trackData;
+
+ public UserSongClip()
+ {
+ }
+
+ public UserSongClip(ATL.Track track)
+ {
+ trackId = new TrackId() { trackId = Path.GetFileNameWithoutExtension(track.Path) };
+ wavPath = track.Path;
+ originalFilePath = track.Path;
+ originalFileName = Path.GetFileNameWithoutExtension(track.Path);
+ trackData = new TrackData()
+ {
+ originalArtist = track.Artist,
+ duration = (float)track.Duration,
+ originalTrackName = track.Title,
+ trackId = trackId,
+ originalFilePath = track.Path
+ };
+ }
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/BeatStructure/VampProcess.cs b/BOXVR Playlist Manager/FitXr/BeatStructure/VampProcess.cs
new file mode 100644
index 0000000..99f6bd6
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/BeatStructure/VampProcess.cs
@@ -0,0 +1,80 @@
+using System;
+using System.Collections;
+using System.Diagnostics;
+using System.IO;
+using System.Text;
+using BoxVR_Playlist_Manager.Helpers;
+
+namespace BoxVR_Playlist_Manager.FitXr.BeatStructure
+{
+ public class VampProcess
+ {
+ public string[] _resultEntries;
+ public StringBuilder output;
+ private Process _process;
+ private const bool SHOW_WINDOW = false;
+ private char APO_LITERAL;
+ private string outputPath;
+ private string vampToolPath;
+ private string vampPluginPath;
+ private bool isProcessComplete;
+
+ public VampProcess()
+ {
+ this.output = new StringBuilder();
+ this.outputPath = Path.Combine(MadmomProcess.madmonOutputPath, "vampOutput.txt");
+ this.vampToolPath = Path.Combine(Paths.StreamingAssetsPath, @"Vamp\sonic-annotator-v1.5-win32\start.bat");
+ this.vampPluginPath = Path.Combine(Paths.StreamingAssetsPath, @"Vamp\Vamp Plugins");
+ this.isProcessComplete = true;
+ }
+
+ public void Apply(
+ string pluginId,
+ string musicPath,
+ Action onFinished)
+ {
+ ApplyCoroutine(pluginId, musicPath, onFinished);
+ }
+
+ public void ApplyCoroutine(
+ string pluginId,
+ string musicPath,
+ Action onFinished)
+ {
+ VampProcess vampProcess = this;
+ File.Delete(vampProcess.outputPath);
+ string str = $" -d {pluginId} \"{musicPath}\" -w csv --csv-force --csv-one-file \"{vampProcess.outputPath}\"";
+ vampProcess._process = new Process();
+ vampProcess._process.StartInfo.FileName = vampProcess.vampToolPath;
+ vampProcess._process.StartInfo.RedirectStandardOutput = true;
+ vampProcess._process.StartInfo.UseShellExecute = false;
+ vampProcess._process.StartInfo.CreateNoWindow = true;
+ vampProcess._process.StartInfo.EnvironmentVariables["VAMP_PATH"] = vampProcess.vampPluginPath;
+ vampProcess._process.StartInfo.Arguments = str;
+ vampProcess._process.EnableRaisingEvents = true;
+ // ISSUE: reference to a compiler-generated method
+ //vampProcess._process.Exited += new EventHandler(vampProcess.\u003CApplyCoroutine\u003Eb__12_0);
+ vampProcess.isProcessComplete = false;
+ vampProcess._process.Start();
+ while(!vampProcess.isProcessComplete && !vampProcess._process.HasExited) { }
+ if((vampProcess._process.ExitCode != 0 ? 0 : (File.Exists(vampProcess.outputPath) ? 1 : 0)) != 0)
+ {
+ vampProcess._resultEntries = File.ReadAllLines(vampProcess.outputPath);
+ vampProcess._process.Close();
+ onFinished.Invoke();
+ }
+ else
+ {
+ vampProcess._process.Close();
+ throw new Exception("PROCESOR_ANALYSISERROR: There was an error analyzing the music file.");
+ }
+ }
+
+ private void OnDestroy()
+ {
+ if(this.isProcessComplete)
+ return;
+ this._process.Kill();
+ }
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/Enums/AudioClipStatus.cs b/BOXVR Playlist Manager/FitXr/Enums/AudioClipStatus.cs
new file mode 100644
index 0000000..df12b0c
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/Enums/AudioClipStatus.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace BoxVR_Playlist_Manager.FitXr.Enums
+{
+ public enum AudioClipStatus
+ {
+ Pending,
+ Loading,
+ Failed,
+ Loaded,
+ WaitForOgg
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/Enums/Game.cs b/BOXVR Playlist Manager/FitXr/Enums/Game.cs
new file mode 100644
index 0000000..27bc081
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/Enums/Game.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace BoxVR_Playlist_Manager.FitXr.Enums
+{
+ public enum Game
+ {
+ BoxVR
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/Enums/LocationMode.cs b/BOXVR Playlist Manager/FitXr/Enums/LocationMode.cs
new file mode 100644
index 0000000..3401cd4
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/Enums/LocationMode.cs
@@ -0,0 +1,11 @@
+namespace BoxVR_Playlist_Manager.FitXr.Enums
+{
+ public enum LocationMode
+ {
+ PlayerData,
+ Workouts,
+ Editor,
+ Downloadable,
+ MyWorkout,
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/Enums/TrackDataState.cs b/BOXVR Playlist Manager/FitXr/Enums/TrackDataState.cs
new file mode 100644
index 0000000..d998499
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/Enums/TrackDataState.cs
@@ -0,0 +1,10 @@
+namespace BoxVR_Playlist_Manager.FitXr.Enums
+{
+ public enum TrackDataState
+ {
+ Idle,
+ Loading,
+ Ready,
+ Failed,
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/Enums/TrackGenre.cs b/BOXVR Playlist Manager/FitXr/Enums/TrackGenre.cs
new file mode 100644
index 0000000..6c7cf3b
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/Enums/TrackGenre.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace BoxVR_Playlist_Manager.FitXr.Enums
+{
+ [Flags]
+ public enum TrackGenre
+ {
+ None = 0,
+ Pop = 1,
+ Rock = 2,
+ Electronic = 4,
+ HipHop = 8,
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/Enums/WorkoutType.cs b/BOXVR Playlist Manager/FitXr/Enums/WorkoutType.cs
new file mode 100644
index 0000000..e0baad9
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/Enums/WorkoutType.cs
@@ -0,0 +1,8 @@
+namespace BoxVR_Playlist_Manager.FitXr.Enums
+{
+ public enum WorkoutType
+ {
+ BoxVR_Workout,
+ BoxVR_Playlist,
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/Models/MusicActionSerializable.cs b/BOXVR Playlist Manager/FitXr/Models/MusicActionSerializable.cs
new file mode 100644
index 0000000..f792871
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/Models/MusicActionSerializable.cs
@@ -0,0 +1,10 @@
+using BoxVR_Playlist_Manager.FitXr.MusicActions;
+
+namespace BoxVR_Playlist_Manager.FitXr.Models
+{
+ public class MusicActionSerializable
+ {
+ public MusicActionType musicActionType;
+ public string musicActionJSON;
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/Models/SerialisedActionList.cs b/BOXVR Playlist Manager/FitXr/Models/SerialisedActionList.cs
new file mode 100644
index 0000000..3ee9f39
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/Models/SerialisedActionList.cs
@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+using Newtonsoft.Json;
+
+namespace BoxVR_Playlist_Manager.FitXr.Models
+{
+ public class MusicActionListSerializable
+ {
+ [JsonProperty("actionList")]
+ public List actionList { get; set; } = new List();
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/Models/SongDefinition.cs b/BOXVR Playlist Manager/FitXr/Models/SongDefinition.cs
new file mode 100644
index 0000000..1414d36
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/Models/SongDefinition.cs
@@ -0,0 +1,105 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using BoxVR_Playlist_Manager.FitXr.BeatStructure;
+using BoxVR_Playlist_Manager.FitXr.MusicActions;
+using Newtonsoft.Json;
+
+namespace BoxVR_Playlist_Manager.FitXr.Models
+{
+ public class SongDefinition
+ {
+ public SongDefinition()
+ {
+ serialisedActionList = new MusicActionListSerializable();
+ }
+
+ [JsonProperty("trackDataName")]
+ public string trackDataName { get; set; }
+
+ //From my minor testing this has always contained an empty list
+ [JsonProperty("serialisedActionList")]
+ public MusicActionListSerializable serialisedActionList { get; set; }
+
+ [JsonIgnore]
+ public List musicActionList;
+ [JsonIgnore]
+ public TrackDefinition trackDefinition { get; set; }
+
+ public void EditMoveAction(MoveChannel channel, float beatNumber, MoveType moveType)
+ {
+ App.logger.Debug("Editing " + (object)channel + "/" + (object)beatNumber + " = " + (object)moveType);
+ MusicActionMoveCue musicActionMoveCue = (MusicActionMoveCue)null;
+ int index1 = -1;
+ for(int index2 = 0; index2 < this.musicActionList.Count; ++index2)
+ {
+ if(this.musicActionList[index2] is MusicActionMoveCue)
+ {
+ MusicActionMoveCue musicAction = (MusicActionMoveCue)this.musicActionList[index2];
+ if((double)musicAction.beatNumber == (double)beatNumber && musicAction.moveAction.moveChannel == channel)
+ {
+ index1 = index2;
+ musicActionMoveCue = musicAction;
+ break;
+ }
+ if(index1 == 0 && (double)this.musicActionList[index2].beatNumber > (double)beatNumber)
+ index1 = index2;
+ }
+ }
+ if(moveType == MoveType.None)
+ {
+ if(index1 != -1)
+ {
+ App.logger.Debug("Deleting entry at " + (object)index1);
+ this.musicActionList.RemoveAt(index1);
+ }
+ }
+ else
+ {
+ if(musicActionMoveCue == null)
+ {
+ musicActionMoveCue = new MusicActionMoveCue(new MoveAction(moveType, channel), beatNumber, 0.0);
+ if(index1 > 0)
+ this.musicActionList.Insert(index1, (MusicAction)musicActionMoveCue);
+ else
+ this.musicActionList.Add((MusicAction)musicActionMoveCue);
+ }
+ App.logger.Debug("modified cue");
+ musicActionMoveCue.moveAction.moveType = moveType;
+ }
+ this.RecalculateCueTiming();
+ }
+
+ public void RecalculateCueTiming()
+ {
+ if(this.trackDefinition == null)
+ this.trackDefinition = TrackDataManager.instance.GetTrackDefinition(this.trackDataName);
+ this.musicActionList.Sort((Comparison)((x, y) => x.beatNumber.CompareTo(y.beatNumber)));
+ float num = 60f / this.trackDefinition.bpm;
+ for(int index = 0; index < this.musicActionList.Count; ++index)
+ {
+ if(this.musicActionList[index] is MusicActionAudio)
+ this.musicActionList[index].startTime = 0.0;
+ if(this.musicActionList[index] is MusicActionBPM)
+ this.musicActionList[index].startTime = 0.0;
+ if(this.musicActionList[index] is MusicActionMoveCue)
+ {
+ MusicActionMoveCue musicAction = (MusicActionMoveCue)this.musicActionList[index];
+ musicAction.startTime = (double)num * (double)musicAction.beatNumber + (double)this.trackDefinition.firstBeatStartDelay;
+ }
+ }
+ }
+
+ public MusicActionAudio GetFirstAudioAction()
+ {
+ if(this.musicActionList == null)
+ return (MusicActionAudio)null;
+ for(int index = 0; index < this.musicActionList.Count; ++index)
+ {
+ if(this.musicActionList[index] is MusicActionAudio)
+ return (MusicActionAudio)this.musicActionList[index];
+ }
+ return (MusicActionAudio)null;
+ }
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/Models/TrackBase.cs b/BOXVR Playlist Manager/FitXr/Models/TrackBase.cs
new file mode 100644
index 0000000..b0cea4e
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/Models/TrackBase.cs
@@ -0,0 +1,18 @@
+using Newtonsoft.Json;
+
+namespace BoxVR_Playlist_Manager.FitXr.Models
+{
+ public class TrackBase
+ {
+ public TrackBase()
+ {
+
+ }
+ [JsonProperty("trackId")]
+ public TrackId trackId { get; set; }
+ [JsonProperty("duration")]
+ public float duration { get; set; }
+ [JsonProperty("bpm")]
+ public float bpm { get; set; }
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/Models/TrackData.cs b/BOXVR Playlist Manager/FitXr/Models/TrackData.cs
new file mode 100644
index 0000000..3785009
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/Models/TrackData.cs
@@ -0,0 +1,132 @@
+using System.IO;
+using BoxVR_Playlist_Manager.FitXr.BeatStructure;
+using BoxVR_Playlist_Manager.FitXr.Enums;
+using BoxVR_Playlist_Manager.Helpers;
+using Newtonsoft.Json;
+
+namespace BoxVR_Playlist_Manager.FitXr.Models
+{
+ public class TrackData : TrackBase
+ {
+ public TrackData()
+ {
+
+ }
+
+
+ [JsonProperty("originalFilePath")]
+ public string originalFilePath { get; set; }
+ [JsonProperty("originalTrackName")]
+ public string originalTrackName { get; set; }
+ [JsonProperty("originalArtist")]
+ public string originalArtist { get; set; }
+ [JsonProperty("firstBeatOffset")]
+ public float firstBeatOffset { get; set; }
+ [JsonProperty("locationMode")]
+ public LocationMode locationMode { get; set; }
+ [JsonProperty("beatStrucureJSON")]
+ public string beatStrucureJSON { get; set; }
+
+ [JsonIgnore]
+ public BeatStructureMadmom beatStructure;
+ [JsonIgnore]
+ public TrackDataState trackDataState;
+
+ public event TrackData.OnTrackDataReady onTrackDataReady;
+
+ public void LoadTrackData(string trackId, LocationMode locationMode)
+ {
+ string str = "";
+ switch(locationMode)
+ {
+ case LocationMode.PlayerData:
+ case LocationMode.Editor:
+ string path = Paths.TrackDataFolder(locationMode) + trackId + ".trackdata.txt";
+ if(File.Exists(path))
+ {
+ str = File.ReadAllText(path);
+ }
+ break;
+ }
+ if(str != "")
+ {
+ TrackData trackData = JsonConvert.DeserializeObject(str);
+ this.originalTrackName = trackData.originalTrackName;
+ this.trackId = trackData.trackId;
+ this.duration = trackData.duration;
+ this.bpm = trackData.bpm;
+ this.locationMode = locationMode;
+ this.beatStrucureJSON = trackData.beatStrucureJSON;
+ this.originalFilePath = trackData.originalFilePath;
+ this.originalArtist = trackData.originalArtist;
+ this.LoadBeatStructure();
+ if(this.onTrackDataReady != null)
+ this.onTrackDataReady(this);
+ this.trackDataState = TrackDataState.Ready;
+ }
+ else
+ {
+ this.trackDataState = TrackDataState.Failed;
+ }
+ }
+
+ public void LoadBeatStructure()
+ {
+ this.beatStructure = TrackDataManager.instance.ReadBeatStructureFromJSON(this.beatStrucureJSON);
+ }
+
+ public string WavFilePath()
+ {
+ string str = "";
+ switch(this.locationMode)
+ {
+ case LocationMode.PlayerData:
+ str = MadmomProcess.madmonOutputPath + this.trackId.trackId + ".wav";
+ break;
+ case LocationMode.Workouts:
+ case LocationMode.MyWorkout:
+ str = this.trackId.trackId.ToString();
+ break;
+ case LocationMode.Editor:
+ str = Paths.WavDataFolder(this.locationMode) + "/" + this.trackId.trackId + ".wav";
+ break;
+ }
+ return str;
+ }
+
+ public void PopulateTrackDataFromAudioFile(
+ string filePath,
+ LocationMode locationMode,
+ Game gameType)
+ {
+ this.trackDataState = TrackDataState.Loading;
+ TrackDataManager.instance.onTrackDataComplete += new TrackDataManager.OnTrackDataComplete(OnTrackDataCreated);
+ if(TrackDataManager.instance.DoesTrackDataExist(filePath, locationMode, gameType))
+ {
+ this.LoadTrackData(new TrackId(filePath).trackId, locationMode);
+ }
+ else
+ {
+ TrackDataManager.instance.PopulateTrackDataFromAudioFile(filePath, locationMode, filePath);
+ }
+ }
+
+ public void OnTrackDataCreated(TrackData trackData)
+ {
+ TrackDataManager.instance.onTrackDataComplete -= new TrackDataManager.OnTrackDataComplete(this.OnTrackDataCreated);
+ this.originalFilePath = trackData.originalFilePath;
+ this.originalTrackName = trackData.originalTrackName;
+ this.originalArtist = trackData.originalArtist;
+ this.trackId = trackData.trackId;
+ this.duration = trackData.duration;
+ this.bpm = trackData.bpm;
+ this.locationMode = trackData.locationMode;
+ this.beatStrucureJSON = trackData.beatStrucureJSON;
+ this.trackDataState = TrackDataState.Ready;
+ onTrackDataReady?.Invoke(this);
+ }
+
+
+ public delegate void OnTrackDataReady(TrackData trackData);
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/Models/TrackDefinition.cs b/BOXVR Playlist Manager/FitXr/Models/TrackDefinition.cs
new file mode 100644
index 0000000..f423e61
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/Models/TrackDefinition.cs
@@ -0,0 +1,135 @@
+using System;
+using System.IO;
+using BoxVR_Playlist_Manager.FitXr.Enums;
+using BoxVR_Playlist_Manager.Helpers;
+using Newtonsoft.Json;
+
+namespace BoxVR_Playlist_Manager.FitXr.Models
+{
+ public class TrackDefinition : TrackBase
+ {
+ public TrackDefinition() => this.audioClipStatus = AudioClipStatus.Pending;
+
+
+ [JsonProperty("firstBeatStartDelay")]
+ public double firstBeatStartDelay { get; set; }
+ [JsonProperty("tagLibArtist")]
+ public string tagLibArtist { get; set; }
+ [JsonProperty("tagLibTitle")]
+ public string tagLibTitle { get; set; }
+ [JsonProperty("genreMask")]
+ public TrackGenre genreMask { get; set; }
+ [JsonProperty("audioClipStatus")]
+ public AudioClipStatus audioClipStatus { get; set; }
+
+ [JsonIgnore]
+ public LocationMode locationMode;
+
+ [JsonIgnore]
+ public byte[] waveTextureData;
+ [JsonIgnore]
+ public TrackData trackData;
+
+ [JsonIgnore]
+ public TimeSpan DurationTimeSpan => TimeSpan.FromSeconds(duration);
+
+
+ public void LoadTrackData()
+ {
+ if(this.trackData != null)
+ return;
+ this.trackData = new TrackData();
+ this.trackData.LoadTrackData(this.trackId.trackId, this.locationMode);
+ }
+
+ public bool LoadTrackDefinition(string trackHash, LocationMode mode)
+ {
+ bool flag = false;
+ this.locationMode = mode;
+
+ string str1 = Paths.DefinitionsFolder(mode) + trackHash + ".wdef";
+ string str2 = "";
+ switch(this.locationMode)
+ {
+ case LocationMode.PlayerData:
+ case LocationMode.Editor:
+ string path = str1 + ".txt";
+ if(System.IO.File.Exists(path))
+ {
+ str2 = System.IO.File.ReadAllText(path);
+ break;
+ }
+ break;
+ //TODO double cheeck as i think it needs .txt
+ case LocationMode.Workouts:
+ case LocationMode.MyWorkout:
+ str2 = System.IO.File.ReadAllText(str1);
+ break;
+ }
+ if(str2 != "")
+ {
+ //TrackDefinition trackDefinition = (TrackDefinition)JsonUtility.FromJson(str2);
+ var trackDefinition = JsonConvert.DeserializeObject(str2);
+ flag = true;
+ this.trackId = trackDefinition.trackId;
+ this.firstBeatStartDelay = trackDefinition.firstBeatStartDelay;
+ this.tagLibTitle = trackDefinition.tagLibTitle;
+ this.tagLibArtist = trackDefinition.tagLibArtist;
+ this.duration = trackDefinition.duration;
+ this.bpm = trackDefinition.bpm;
+ }
+ return flag;
+ }
+
+ public bool SaveTrackDefinition()
+ {
+ if(this.locationMode == LocationMode.MyWorkout || this.locationMode == LocationMode.Workouts)
+ {
+ App.logger.Error("Cant save to resourses you potatoe head !!!!");
+ return false;
+ }
+ Directory.CreateDirectory(Paths.DefinitionsFolder(this.locationMode));
+ //string json = JsonUtility.ToJson((object)this, true);
+ string json = JsonConvert.SerializeObject(this);
+ File.WriteAllText(Paths.DefinitionsFolder(this.locationMode) + this.trackId.trackId + ".wdef.txt", json);
+ return true;
+ }
+
+ //public IEnumerator GenerateWavImageData(int numSamples = 131071)
+ //{
+ // if(this.audioClipStatus != AudioClipStatus.Loaded)
+ // yield return (object)this.LoadAudioClip();
+ // while(this.audioClipStatus != AudioClipStatus.Loaded || this.audioClipStatus != AudioClipStatus.Failed)
+ // yield return (object)null;
+ // if(Object.op_Implicit((Object)this.audioClip))
+ // {
+ // Debug.Log((object)"generating wave image data");
+ // this.waveTextureData = new byte[numSamples];
+ // float[] audioSamples = new float[this.audioClip.get_samples() * this.audioClip.get_channels()];
+ // this.audioClip.LoadAudioData();
+ // while(this.audioClip.get_loadState() != 2)
+ // yield return (object)null;
+ // this.audioClip.GetData(audioSamples, 0);
+ // double num1 = (double)(audioSamples.Length / this.waveTextureData.Length);
+ // for(int index1 = 0; index1 < this.waveTextureData.Length; ++index1)
+ // {
+ // double num2 = (double)index1 * num1 - num1 / 2.0;
+ // double num3 = num2 + num1;
+ // float num4 = 0.0f;
+ // for(int index2 = (int)num2; index2 < (int)num3; ++index2)
+ // {
+ // if(index2 > 0 && index2 < audioSamples.Length && (double)Mathf.Abs(audioSamples[index2]) > (double)num4)
+ // num4 = Mathf.Abs(audioSamples[index2]);
+ // }
+ // this.waveTextureData[index1] = (byte)((double)num4 * 256.0);
+ // }
+ // if(this.locationMode == LocationMode.Editor)
+ // {
+ // File.WriteAllBytes(Paths.ImgDataFolder(this.locationMode) + this.trackId.trackId + ".imgdata.bytes", this.waveTextureData);
+ // Debug.Log((object)"Written wave image data");
+ // }
+ // audioSamples = (float[])null;
+ // }
+ //}
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/Models/TrackId.cs b/BOXVR Playlist Manager/FitXr/Models/TrackId.cs
new file mode 100644
index 0000000..116c212
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/Models/TrackId.cs
@@ -0,0 +1,23 @@
+using System.IO;
+using BoxVR_Playlist_Manager.FitXr.Utility;
+using Newtonsoft.Json;
+
+namespace BoxVR_Playlist_Manager.FitXr.Models
+{
+ public class TrackId
+ {
+ public TrackId()
+ {
+ }
+ public TrackId(string fullFilePath)
+ {
+ if(fullFilePath.Contains("/TrackData/"))
+ this.trackId = Path.GetFileNameWithoutExtension(fullFilePath);
+ else
+ this.trackId = MD5.MD5Sum(Path.GetFileNameWithoutExtension(fullFilePath));
+ }
+
+ [JsonProperty("trackId")]
+ public string trackId { get; set; }
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/Models/WorkoutId.cs b/BOXVR Playlist Manager/FitXr/Models/WorkoutId.cs
new file mode 100644
index 0000000..8458f6e
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/Models/WorkoutId.cs
@@ -0,0 +1,38 @@
+using System;
+
+namespace BoxVR_Playlist_Manager.FitXr.Models
+{
+ public struct WorkoutId : IEquatable
+ {
+ public string _id;
+
+ public WorkoutId(string id) => this._id = id;
+
+ public static bool operator ==(WorkoutId obj1, WorkoutId obj2)
+ {
+ if((ValueType)obj1 == (ValueType)obj2)
+ return true;
+ return (ValueType)obj1 != null && (ValueType)obj2 != null && obj1._id == obj2._id;
+ }
+
+ public static bool operator !=(WorkoutId obj1, WorkoutId obj2) => !(obj1 == obj2);
+
+ public bool Equals(WorkoutId other)
+ {
+ if((ValueType)other == null)
+ return false;
+ return (ValueType)this == (ValueType)other || this._id.Equals(other._id);
+ }
+
+ public override bool Equals(object obj)
+ {
+ if(obj == null)
+ return false;
+ if((ValueType)this == obj)
+ return true;
+ return obj.GetType() == this.GetType() && this.Equals((WorkoutId)obj);
+ }
+
+ public override int GetHashCode() => this._id.GetHashCode();
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/Models/WorkoutInfo.cs b/BOXVR Playlist Manager/FitXr/Models/WorkoutInfo.cs
new file mode 100644
index 0000000..2141015
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/Models/WorkoutInfo.cs
@@ -0,0 +1,57 @@
+using BoxVR_Playlist_Manager.FitXr.Enums;
+using Newtonsoft.Json;
+
+namespace BoxVR_Playlist_Manager.FitXr.Models
+{
+ public class WorkoutInfo
+ {
+ [JsonProperty("workoutName")]
+ public string workoutName { get; set; }
+ [JsonProperty("game")]
+ public Game game { get; set; }
+ [JsonProperty("workoutStyle")]
+ public int workoutStyle { get; set; }
+ [JsonProperty("authorName")]
+ public string authorName { get; set; }
+ [JsonProperty("trackGenre")]
+ public TrackGenre trackGenre { get; set; }
+ [JsonProperty("leaderboardId")]
+ public string leaderboardId { get; set; } = "undefined";
+ [JsonProperty("sonyLeaderboardId")]
+ public int sonyLeaderboardId { get; set; } = -1;
+ [JsonProperty("workoutId")]
+ public string workoutId { get; set; }
+ [JsonProperty("hasSquats")]
+ public bool hasSquats { get; set; }
+ [JsonProperty("hasJumps")]
+ public bool hasJumps { get; set; }
+ [JsonProperty("workoutType")]
+ public WorkoutType workoutType { get; set; }
+ [JsonProperty("duration")]
+ public float duration { get; set; }
+ [JsonIgnore]
+ public string originalName { get; set; }
+
+ public WorkoutInfo()
+ {
+
+ }
+
+ public WorkoutInfo(WorkoutInfo workoutInfo)
+ {
+ workoutName = workoutInfo.workoutName;
+ originalName = workoutInfo.originalName ?? workoutInfo.workoutName;
+ game = workoutInfo.game;
+ workoutStyle = workoutInfo.workoutStyle;
+ authorName = workoutInfo.authorName;
+ trackGenre = workoutInfo.trackGenre;
+ leaderboardId = workoutInfo.leaderboardId;
+ sonyLeaderboardId = workoutInfo.sonyLeaderboardId;
+ workoutId = workoutInfo.workoutId;
+ hasSquats = workoutInfo.hasSquats;
+ hasJumps = workoutInfo.hasJumps;
+ workoutType = workoutInfo.workoutType;
+ duration = workoutInfo.duration;
+ }
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/Models/WorkoutPlaylist.cs b/BOXVR Playlist Manager/FitXr/Models/WorkoutPlaylist.cs
new file mode 100644
index 0000000..d76d39c
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/Models/WorkoutPlaylist.cs
@@ -0,0 +1,162 @@
+using System.Collections.Generic;
+using System.IO;
+using BoxVR_Playlist_Manager.FitXr.BeatStructure;
+using BoxVR_Playlist_Manager.FitXr.Enums;
+using BoxVR_Playlist_Manager.FitXr.MusicActions;
+using Newtonsoft.Json;
+
+namespace BoxVR_Playlist_Manager.FitXr.Models
+{
+ public class WorkoutPlaylist
+ {
+ public WorkoutPlaylist(WorkoutPlaylist playlist)
+ {
+ definition = new WorkoutInfo(playlist.definition);
+
+ songs = new List(playlist.songs);
+ }
+
+ public WorkoutPlaylist()
+ {
+ definition = new WorkoutInfo();
+ songs = new List();
+ }
+
+ [JsonProperty("definition")]
+ public WorkoutInfo definition { get; set; }
+
+ [JsonProperty("songs")]
+ public List songs { get; set; }
+
+ public WorkoutId workoutId => new WorkoutId(this.definition.workoutName.Replace("_", "").Replace(" ", ""));
+
+ public void SaveToPath(string path)
+ {
+ if(!Directory.Exists(path))
+ {
+ App.logger.Debug("Path failure");
+ }
+ else
+ {
+ for(int index = 0; index < this.songs.Count; ++index)
+ {
+ if(this.songs[index].musicActionList != null)
+ {
+ this.songs[index].serialisedActionList = new MusicActionListSerializable();
+ this.songs[index].serialisedActionList.actionList = MusicActionListSerializer.Instance.BuildSerializableMusicActionList(this.songs[index].musicActionList);
+ }
+ }
+ File.WriteAllText(path + this.definition.workoutName + ".workoutplaylist.txt", JsonConvert.SerializeObject(this));
+ }
+ }
+
+ public void Save(LocationMode locationMode)
+ {
+ this.PreSave();
+ WorkoutDefinitionManager.instance.SaveWorkout(this, locationMode);
+ }
+
+ private void PreSave()
+ {
+ this.definition.hasSquats = false;
+ this.definition.trackGenre = TrackGenre.None;
+ this.definition.duration = 0.0f;
+ label_9:
+ for(int index1 = 0; index1 < this.songs.Count; ++index1)
+ {
+ this.definition.trackGenre |= this.songs[index1].trackDefinition.genreMask;
+ this.definition.duration += this.songs[index1].trackDefinition.duration;
+ if(!this.definition.hasSquats)
+ {
+ for(int index2 = 0; index2 < this.songs[index1].musicActionList.Count; ++index2)
+ {
+ if(this.songs[index1].musicActionList[index2] is MusicActionMoveCue)
+ {
+ switch(((MusicActionMoveCue)this.songs[index1].musicActionList[index2]).moveAction.moveType)
+ {
+ case MoveType.Boxing_Dodge:
+ case MoveType.Boxing_Squat:
+ this.definition.hasSquats = true;
+ goto label_9;
+ default:
+ continue;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public void LoadFromJSON(string jsonString)
+ {
+ if(string.IsNullOrEmpty(jsonString))
+ return;
+ WorkoutPlaylist workoutPlaylist = JsonConvert.DeserializeObject(jsonString);
+ if(workoutPlaylist == null)
+ return;
+ this.definition = workoutPlaylist.definition;
+ this.songs = workoutPlaylist.songs;
+ for(int index = 0; index < this.songs.Count; ++index)
+ this.songs[index].musicActionList = MusicActionListSerializer.instance.ReadSerializedActionList(this.songs[index].serialisedActionList);
+ }
+
+ public void LoadFromPath(string path) => this.LoadFromJSON(this.WorkoutJsonFromPath(path));
+
+ public string WorkoutJsonFromPath(string workoutPath)
+ {
+ string str = "";
+ if((!File.Exists(workoutPath) ? 0 : (new FileInfo(workoutPath).Length > 0L ? 1 : 0)) != 0)
+ str = File.ReadAllText(workoutPath);
+ return str;
+ }
+
+ public SongDefinition AddSong(TrackId trackId)
+ {
+ if(this.songs == null)
+ this.songs = new List();
+ SongDefinition songDefinition = new SongDefinition();
+ songDefinition.trackDataName = trackId.trackId;
+ this.songs.Add(songDefinition);
+ return songDefinition;
+ }
+
+ public void AddSong(SongDefinition song)
+ {
+ if(this.songs == null)
+ this.songs = new List();
+ this.songs.Add(song);
+ }
+
+ public void RemoveSong(int index)
+ {
+ string trackDataName = this.songs[index].trackDataName;
+ this.songs.RemoveAt(index);
+ }
+
+ public bool RemoveSong(SongDefinition song)
+ {
+ return songs.Remove(song);
+ }
+
+ public float CalcTotalLength()
+ {
+ float num = 0.0f;
+ if(this.songs != null)
+ {
+ for(int index = 0; index < this.songs.Count; ++index)
+ {
+ if(this.songs[index].trackDefinition != null)
+ {
+ num += this.songs[index].trackDefinition.duration;
+ }
+ else
+ {
+ this.songs[index].trackDefinition = TrackDataManager.instance.GetTrackDefinition(this.songs[index].trackDataName);
+ }
+ }
+ }
+ this.definition.duration = num;
+ return num;
+ }
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/MusicActionListSerializer.cs b/BOXVR Playlist Manager/FitXr/MusicActionListSerializer.cs
new file mode 100644
index 0000000..25df024
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/MusicActionListSerializer.cs
@@ -0,0 +1,132 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using BoxVR_Playlist_Manager.FitXr.Models;
+using BoxVR_Playlist_Manager.FitXr.MusicActions;
+using BoxVR_Playlist_Manager.Helpers;
+using Newtonsoft.Json;
+
+namespace BoxVR_Playlist_Manager.FitXr
+{
+ public class MusicActionListSerializer
+ {
+ public static MusicActionListSerializer instance = instance == null ? new MusicActionListSerializer() : instance;
+
+ public static MusicActionListSerializer Instance => instance;
+
+ public void SaveWorkoutActionSequence(
+ List musicActions,
+ string workoutName,
+ string difficulty = "")
+ {
+ string str = Paths.PersistentDataPath + "/Playlists/Workouts/";
+ string path = Path.Combine(str, workoutName + "_" + difficulty + ".actions");
+ if(difficulty == "")
+ {
+ str = Paths.PersistentDataPath + "/Workouts/";
+ path = Path.Combine(str, workoutName + ".actions");
+ }
+ if(!Directory.Exists(str))
+ Directory.CreateDirectory(str);
+ List actionSerializableList = this.BuildSerializableMusicActionList(musicActions);
+ string json = JsonConvert.SerializeObject(new MusicActionListSerializable()
+ {
+ actionList = actionSerializableList
+ });
+ File.WriteAllText(path, json);
+ }
+
+ public List LoadWorkoutActionSequence(
+ string workoutName,
+ string difficulty = "")
+ {
+ List musicActionList = new List();
+ string str = Paths.PersistentDataPath + "/Playlists/Workouts/" + workoutName + "_" + difficulty + ".actions";
+ if(difficulty == "")
+ str = Paths.PersistentDataPath + "/Workouts/" + workoutName + ".actions";
+ return (!File.Exists(str) ? 0 : (new FileInfo(str).Length > 0L ? 1 : 0)) != 0 ? this.ReadSerializedActionList(JsonConvert.DeserializeObject(File.ReadAllText(str))) : (List)null;
+ }
+
+ public List ReadSerializedActionList(
+ MusicActionListSerializable listWrapper)
+ {
+ List musicActionList = new List();
+ if(listWrapper == null)
+ {
+ return musicActionList;
+ }
+ for(int index = 0; index < listWrapper.actionList.Count; ++index)
+ {
+ switch(listWrapper.actionList[index].musicActionType)
+ {
+ case MusicActionType.MoveCue:
+ MusicActionMoveCue musicActionMoveCue = JsonConvert.DeserializeObject(listWrapper.actionList[index].musicActionJSON);
+ musicActionMoveCue.musicActiontype = MusicActionType.MoveCue;
+ musicActionList.Add((MusicAction)musicActionMoveCue);
+ break;
+ case MusicActionType.Message:
+ MusicActionMessage musicActionMessage = JsonConvert.DeserializeObject(listWrapper.actionList[index].musicActionJSON);
+ musicActionMessage.musicActiontype = MusicActionType.Message;
+ musicActionList.Add((MusicAction)musicActionMessage);
+ break;
+ case MusicActionType.BPM:
+ MusicActionBPM musicActionBpm = JsonConvert.DeserializeObject(listWrapper.actionList[index].musicActionJSON);
+ musicActionBpm.musicActiontype = MusicActionType.BPM;
+ musicActionList.Add((MusicAction)musicActionBpm);
+ break;
+ case MusicActionType.Audio:
+ MusicActionAudio musicActionAudio = JsonConvert.DeserializeObject(listWrapper.actionList[index].musicActionJSON);
+ musicActionAudio.musicActiontype = MusicActionType.Audio;
+ musicActionList.Add((MusicAction)musicActionAudio);
+ break;
+ }
+ }
+ return musicActionList;
+ }
+
+ public List BuildSerializableMusicActionList(
+ List musicActions)
+ {
+ List actionSerializableList = new List();
+ for(int index = 0; index < musicActions.Count; ++index)
+ {
+ MusicActionSerializable actionSerializable = new MusicActionSerializable();
+ if(musicActions[index] is MusicActionMoveCue)
+ actionSerializable.musicActionType = MusicActionType.MoveCue;
+ else if(musicActions[index] is MusicActionMessage)
+ actionSerializable.musicActionType = MusicActionType.Message;
+ else if(musicActions[index] is MusicActionBPM)
+ actionSerializable.musicActionType = MusicActionType.BPM;
+ else if(musicActions[index] is MusicActionAudio)
+ actionSerializable.musicActionType = MusicActionType.Audio;
+ actionSerializable.musicActionJSON = this.SerializeMusicAction(musicActions[index], actionSerializable.musicActionType);
+ actionSerializableList.Add(actionSerializable);
+ }
+ return actionSerializableList;
+ }
+
+ private string SerializeMusicAction(MusicAction musicAction, MusicActionType musicActionType)
+ {
+ string str = "";
+ switch(musicActionType)
+ {
+ case MusicActionType.MoveCue:
+ str = JsonConvert.SerializeObject((MusicActionMoveCue)musicAction);
+ break;
+ case MusicActionType.Message:
+ str = JsonConvert.SerializeObject((MusicActionMessage)musicAction);
+ break;
+ case MusicActionType.BPM:
+ str = JsonConvert.SerializeObject((MusicActionBPM)musicAction);
+ break;
+ case MusicActionType.Audio:
+ str = JsonConvert.SerializeObject((MusicActionAudio)musicAction);
+ break;
+ }
+ return str;
+ }
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/MusicActions/MessagePosition.cs b/BOXVR Playlist Manager/FitXr/MusicActions/MessagePosition.cs
new file mode 100644
index 0000000..dd1becf
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/MusicActions/MessagePosition.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace BoxVR_Playlist_Manager.FitXr.MusicActions
+{
+ public enum MessagePosition
+ {
+ CENTER,
+ LEFT,
+ RIGHT,
+ BOTH,
+ NONE,
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/MusicActions/MoveAction.cs b/BOXVR Playlist Manager/FitXr/MusicActions/MoveAction.cs
new file mode 100644
index 0000000..ea565af
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/MusicActions/MoveAction.cs
@@ -0,0 +1,18 @@
+namespace BoxVR_Playlist_Manager.FitXr.MusicActions
+{
+ public class MoveAction
+ {
+ public MoveType moveType;
+ public MoveChannel moveChannel;
+ public int sequenceBeat;
+ public float actionOffset;
+
+ public MoveAction(MoveType type, MoveChannel channel)
+ {
+ this.moveType = type;
+ this.moveChannel = channel;
+ }
+
+ public override string ToString() => this.moveChannel.ToString() + "_" + this.moveType.ToString();
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/MusicActions/MoveChannel.cs b/BOXVR Playlist Manager/FitXr/MusicActions/MoveChannel.cs
new file mode 100644
index 0000000..cd398ce
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/MusicActions/MoveChannel.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace BoxVR_Playlist_Manager.FitXr.MusicActions
+{
+ public enum MoveChannel
+ {
+ BoxVR_Front,
+ BoxVR_Front_Low,
+ BoxVR_Center,
+ BoxVR_Center_Low,
+ BoxVR_Back,
+ BoxVR_Back_Low,
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/MusicActions/MoveType.cs b/BOXVR Playlist Manager/FitXr/MusicActions/MoveType.cs
new file mode 100644
index 0000000..054f466
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/MusicActions/MoveType.cs
@@ -0,0 +1,15 @@
+namespace BoxVR_Playlist_Manager.FitXr.MusicActions
+{
+ public enum MoveType
+ {
+ None = 0,
+ Rest = 1,
+ StanceChange = 2,
+ Boxing_Block = 100, // 0x00000064
+ Boxing_Jab = 101, // 0x00000065
+ Boxing_Hook = 102, // 0x00000066
+ Boxing_UpperCut = 103, // 0x00000067
+ Boxing_Dodge = 104, // 0x00000068
+ Boxing_Squat = 105, // 0x00000069
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/MusicActions/MusicAction.cs b/BOXVR Playlist Manager/FitXr/MusicActions/MusicAction.cs
new file mode 100644
index 0000000..1783194
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/MusicActions/MusicAction.cs
@@ -0,0 +1,13 @@
+namespace BoxVR_Playlist_Manager.FitXr.MusicActions
+{
+ public class MusicAction
+ {
+ public double startTime;
+ public float beatNumber;
+ public MusicActionType musicActiontype;
+
+ public virtual void Perform()
+ {
+ }
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/MusicActions/MusicActionAudio.cs b/BOXVR Playlist Manager/FitXr/MusicActions/MusicActionAudio.cs
new file mode 100644
index 0000000..6859389
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/MusicActions/MusicActionAudio.cs
@@ -0,0 +1,20 @@
+namespace BoxVR_Playlist_Manager.FitXr.MusicActions
+{
+ public class MusicActionAudio : MusicAction
+ {
+ public string wavFilePath;
+ public string trackId;
+
+ public MusicActionAudio(string wavPath, string trackid, double startOffset)
+ {
+ this.wavFilePath = wavPath;
+ this.startTime = startOffset;
+ this.trackId = trackid;
+ this.musicActiontype = MusicActionType.Audio;
+ }
+
+ public override void Perform()
+ {
+ }
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/MusicActions/MusicActionBPM.cs b/BOXVR Playlist Manager/FitXr/MusicActions/MusicActionBPM.cs
new file mode 100644
index 0000000..5895407
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/MusicActions/MusicActionBPM.cs
@@ -0,0 +1,14 @@
+namespace BoxVR_Playlist_Manager.FitXr.MusicActions
+{
+ public class MusicActionBPM : MusicAction
+ {
+ public float segmentBPM;
+
+ public MusicActionBPM(float trackBPM, double offsetOnArrival)
+ {
+ this.segmentBPM = trackBPM;
+ this.startTime = offsetOnArrival;
+ this.musicActiontype = MusicActionType.BPM;
+ }
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/MusicActions/MusicActionMessage.cs b/BOXVR Playlist Manager/FitXr/MusicActions/MusicActionMessage.cs
new file mode 100644
index 0000000..1cafbf7
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/MusicActions/MusicActionMessage.cs
@@ -0,0 +1,18 @@
+namespace BoxVR_Playlist_Manager.FitXr.MusicActions
+{
+ public class MusicActionMessage : MusicAction
+ {
+ public MessagePosition position;
+ public string message;
+
+ public MusicActionMessage(
+ string message,
+ MessagePosition position,
+ double displayTime)
+ {
+ this.message = message;
+ this.position = position;
+ this.startTime = displayTime;
+ }
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/MusicActions/MusicActionMoveCue.cs b/BOXVR Playlist Manager/FitXr/MusicActions/MusicActionMoveCue.cs
new file mode 100644
index 0000000..c345c8b
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/MusicActions/MusicActionMoveCue.cs
@@ -0,0 +1,17 @@
+namespace BoxVR_Playlist_Manager.FitXr.MusicActions
+{
+ public class MusicActionMoveCue : MusicAction
+ {
+ public MoveAction moveAction;
+
+ public MusicActionMoveCue(MoveAction action, float beat, double time)
+ {
+ this.moveAction = action;
+ this.startTime = time;
+ this.beatNumber = beat;
+ this.musicActiontype = MusicActionType.MoveCue;
+ }
+
+ //public override void Perform() => MoveCueFactoryBoxVR.instance.Spawn(this.moveAction);
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/MusicActions/MusicActionType.cs b/BOXVR Playlist Manager/FitXr/MusicActions/MusicActionType.cs
new file mode 100644
index 0000000..e9ad089
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/MusicActions/MusicActionType.cs
@@ -0,0 +1,10 @@
+namespace BoxVR_Playlist_Manager.FitXr.MusicActions
+{
+ public enum MusicActionType
+ {
+ MoveCue,
+ Message,
+ BPM,
+ Audio,
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/ReadOnlyDictionary.cs b/BOXVR Playlist Manager/FitXr/ReadOnlyDictionary.cs
new file mode 100644
index 0000000..a4358e9
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/ReadOnlyDictionary.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace BoxVR_Playlist_Manager.FitXr
+{
+ [Serializable]
+ public class ReadOnlyDictionary :
+ IDictionary,
+ ICollection>,
+ IEnumerable>,
+ IEnumerable
+ {
+ private readonly IDictionary _dictionary;
+
+ public ReadOnlyDictionary() => this._dictionary = (IDictionary)new Dictionary();
+
+ public ReadOnlyDictionary(IDictionary dictionary) => this._dictionary = dictionary;
+
+ void IDictionary.Add(TKey key, TValue value) => throw ReadOnlyDictionary.ReadOnlyException();
+
+ public bool ContainsKey(TKey key) => this._dictionary.ContainsKey(key);
+
+ public ICollection Keys => this._dictionary.Keys;
+
+ bool IDictionary.Remove(TKey key) => throw ReadOnlyDictionary.ReadOnlyException();
+
+ public bool TryGetValue(TKey key, out TValue value) => this._dictionary.TryGetValue(key, out value);
+
+ public ICollection Values => this._dictionary.Values;
+
+ public TValue this[TKey key] => this._dictionary[key];
+
+ TValue IDictionary.this[TKey key]
+ {
+ get => this[key];
+ set => throw ReadOnlyDictionary.ReadOnlyException();
+ }
+
+ void ICollection>.Add(
+ KeyValuePair item)
+ {
+ throw ReadOnlyDictionary.ReadOnlyException();
+ }
+
+ void ICollection>.Clear() => throw ReadOnlyDictionary.ReadOnlyException();
+
+ public bool Contains(KeyValuePair item) => this._dictionary.Contains(item);
+
+ public void CopyTo(KeyValuePair[] array, int arrayIndex) => this._dictionary.CopyTo(array, arrayIndex);
+
+ public int Count => this._dictionary.Count;
+
+ public bool IsReadOnly => true;
+
+ bool ICollection>.Remove(
+ KeyValuePair item)
+ {
+ throw ReadOnlyDictionary.ReadOnlyException();
+ }
+
+ public IEnumerator> GetEnumerator() => this._dictionary.GetEnumerator();
+
+ IEnumerator IEnumerable.GetEnumerator() => (IEnumerator)this.GetEnumerator();
+
+ private static Exception ReadOnlyException() => (Exception)new NotSupportedException("This dictionary is read-only");
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/Tools/Format.cs b/BOXVR Playlist Manager/FitXr/Tools/Format.cs
new file mode 100644
index 0000000..2e2829d
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/Tools/Format.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace BoxVR_Playlist_Manager.FitXr.Tools
+{
+ public class Format
+ {
+ public static string SecondsToString(float value) => string.Format("{0:0}m:{1:00}s", (object)Math.Floor(value / 60f), (object)(value % 60f));
+
+ public static void FloatsFromCsvLine(string entry, out float val1, out float val2)
+ {
+ string[] strArray = entry.Split(',');
+ float.TryParse(strArray[1], out val1);
+ float.TryParse(strArray[2], out val2);
+ }
+
+ public static IEnumerable> CreateSortedDictionary(
+ ReadOnlyDictionary unsortedDic)
+ {
+ return unsortedDic.OrderBy, int>((Func, int>)(pair => pair.Key)).Take>(unsortedDic.Count);
+ }
+
+ public static int[] Long2DoubleInt(long a) => new int[2]
+ {
+ (int) (a & (long) uint.MaxValue),
+ (int) (a >> 32)
+ };
+
+ public static long DoubleIntToLong(int[] a) => (long)a[1] << 32 | (long)(uint)a[0];
+
+ public static float LbsToKg(float lbs) => lbs * 0.453592f;
+
+ public static float CmToFeet(float cm) => cm * 0.0328084f;
+
+ public static float FeetToInch(float feet) => feet * 12f;
+
+ public static DateTime StartOfWeek(DateTime dt, DayOfWeek startOfWeek)
+ {
+ int num = (7 + (dt.DayOfWeek - startOfWeek)) % 7;
+ return dt.AddDays((double)(-1 * num)).Date;
+ }
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/Tools/IO.cs b/BOXVR Playlist Manager/FitXr/Tools/IO.cs
new file mode 100644
index 0000000..54e7fee
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/Tools/IO.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace BoxVR_Playlist_Manager.FitXr.Tools
+{
+ public class IO
+ {
+ public static void WriteStringToDisk(string filename, string content)
+ {
+ if(File.Exists(filename))
+ File.Delete(filename);
+ StreamWriter text = File.CreateText(filename);
+ text.Write(content);
+ text.Close();
+ }
+
+ public static void DeleteFilesWithExtension(string directory, string extension)
+ {
+ foreach(FileInfo fileInfo in ((IEnumerable)new DirectoryInfo(directory).GetFiles("*." + extension)).Where((Func)(p => p.Extension == "." + extension)).ToArray())
+ {
+ try
+ {
+ fileInfo.Attributes = FileAttributes.Normal;
+ File.Delete(fileInfo.FullName);
+ }
+ catch
+ {
+ }
+ }
+ }
+
+ public static string[] GetVisibleFiles(string path, string fileFilter)
+ {
+ List stringList = new List();
+ DirectoryInfo directoryInfo = new DirectoryInfo(path);
+ string str = fileFilter;
+ char[] chArray = new char[1] { ';' };
+ foreach(string searchPattern in str.Split(chArray))
+ {
+ foreach(FileInfo fileInfo in ((IEnumerable)directoryInfo.GetFiles(searchPattern)).Where((Func)(f => (f.Attributes & FileAttributes.Hidden) != FileAttributes.Hidden)))
+ stringList.Add(fileInfo.FullName);
+ }
+ stringList.Sort();
+ return stringList.ToArray();
+ }
+
+ public static string[] GetDrives() => Directory.GetLogicalDrives();
+
+ public static string[] GetVisibleDirectories(string path)
+ {
+ string[] directories = Directory.GetDirectories(path);
+ List stringList = new List();
+ foreach(string path2 in directories)
+ {
+ string path1 = Path.Combine(path, path2);
+ if((new DirectoryInfo(path1).Attributes & FileAttributes.Hidden) != FileAttributes.Hidden)
+ stringList.Add(path1);
+ }
+ return ((IEnumerable)directories).ToArray();
+ }
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/Utility/MD5.cs b/BOXVR Playlist Manager/FitXr/Utility/MD5.cs
new file mode 100644
index 0000000..4f8a536
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/Utility/MD5.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Text;
+using System.Security.Cryptography;
+
+namespace BoxVR_Playlist_Manager.FitXr.Utility
+{
+ public sealed class MD5
+ {
+ public static string MD5Sum(string strToEncrypt) => MD5.Md5Sum(strToEncrypt);
+
+ public static string Md5Sum(string strToEncrypt)
+ {
+ byte[] hash = new MD5CryptoServiceProvider().ComputeHash(new UTF8Encoding().GetBytes(strToEncrypt));
+ string str = "";
+ for(int index = 0; index < hash.Length; ++index)
+ str += Convert.ToString(hash[index], 16).PadLeft(2, '0');
+ return str.PadLeft(32, '0');
+ }
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/Utility/ShuffleList.cs b/BOXVR Playlist Manager/FitXr/Utility/ShuffleList.cs
new file mode 100644
index 0000000..1df9902
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/Utility/ShuffleList.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+
+namespace BoxVR_Playlist_Manager.FitXr.Utility
+{
+ public static class ShuffleList
+ {
+ public static void Shuffle(this IList list)
+ {
+ Random random = new Random();
+ int count = list.Count;
+ while(count > 1)
+ {
+ --count;
+ int index = random.Next(count + 1);
+ T obj = list[index];
+ list[index] = list[count];
+ list[count] = obj;
+ }
+ }
+ }
+}
diff --git a/BOXVR Playlist Manager/FitXr/WorkoutDefinitionManager.cs b/BOXVR Playlist Manager/FitXr/WorkoutDefinitionManager.cs
new file mode 100644
index 0000000..459261e
--- /dev/null
+++ b/BOXVR Playlist Manager/FitXr/WorkoutDefinitionManager.cs
@@ -0,0 +1,105 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using BoxVR_Playlist_Manager.FitXr.Enums;
+using BoxVR_Playlist_Manager.FitXr.Models;
+using BoxVR_Playlist_Manager.Helpers;
+using Newtonsoft.Json;
+
+namespace BoxVR_Playlist_Manager.FitXr
+{
+ public class WorkoutDefinitionManager
+ {
+ public static WorkoutDefinitionManager instance = instance == null ? new WorkoutDefinitionManager() : instance;
+ private Game currentGameType;
+ private string currentpath;
+ public bool workoutsLoaded;
+
+ public List workoutList { get; private set; }
+
+ public WorkoutDefinitionManager()
+ {
+ if(MusicActionListSerializer.instance == null)
+ return;
+ }
+
+ public void LoadWorkoutPlaylists(LocationMode loadingMode, Game gameType, bool additive = false)
+ {
+ if(additive)
+ {
+ if(this.workoutList == null)
+ this.workoutList = new List();
+ }
+ else
+ this.workoutList = new List();
+ this.currentGameType = gameType;
+ this.workoutsLoaded = false;
+ this.SetCurrentPath(loadingMode);
+ switch(loadingMode)
+ {
+ case LocationMode.PlayerData:
+ case LocationMode.Editor:
+ LoadFromDataPath();
+ break;
+ default:
+ throw new Exception($"{currentGameType} NOT SUPPORTED");
+ }
+ }
+
+ public void UpdateWorkoutList(List newWorkouts)
+ {
+ for(int index = 0; index < newWorkouts.Count; ++index)
+ {
+ if(!this.workoutList.Contains(newWorkouts[index]))
+ this.workoutList.Add(newWorkouts[index]);
+ }
+ this.workoutList.Sort((Comparison)((p1, p2) => p1.definition.duration.CompareTo(p2.definition.duration)));
+ }
+
+ private void SetCurrentPath(LocationMode locationMode) => this.currentpath = Paths.WorkoutDefinitionFolder(locationMode, Game.BoxVR);
+
+ private void LoadFromDataPath()
+ {
+ if(Directory.Exists(this.currentpath))
+ {
+ foreach(FileSystemInfo file in new DirectoryInfo(this.currentpath).GetFiles("*.txt"))
+ {
+ WorkoutPlaylist workoutPlaylist = new WorkoutPlaylist();
+ string jsonString = File.ReadAllText(file.FullName);
+ workoutPlaylist.LoadFromJSON(jsonString);
+ if(!this.workoutList.Contains(workoutPlaylist))
+ this.workoutList.Add(workoutPlaylist);
+ }
+ this.workoutList.Sort((Comparison)((p1, p2) => p1.definition.duration.CompareTo(p2.definition.duration)));
+ }
+ this.workoutsLoaded = true;
+ }
+
+ public void SaveWorkout(WorkoutPlaylist workout, LocationMode savingMode)
+ {
+ string str = Paths.WorkoutDefinitionFolder(savingMode, workout.definition.game);
+ for(int index = 0; index < workout.songs.Count; ++index)
+ {
+ workout.songs[index].serialisedActionList = new MusicActionListSerializable();
+ workout.songs[index].serialisedActionList.actionList = MusicActionListSerializer.Instance.BuildSerializableMusicActionList(workout.songs[index].musicActionList);
+ }
+ this.currentpath = str + "/" + workout.definition.workoutName + ".workoutplaylist.txt";
+ File.WriteAllText(this.currentpath, JsonConvert.SerializeObject(workout));
+ }
+
+ public WorkoutPlaylist GetWorkoutByIDOrName(string nameOrID)
+ {
+ if(this.workoutList == null)
+ {
+ App.logger.Debug("Workout List not loaded");
+ return (WorkoutPlaylist)null;
+ }
+ for(int index = 0; index < this.workoutList.Count; ++index)
+ {
+ if(nameOrID == this.workoutList[index].workoutId._id || nameOrID == this.workoutList[index].definition.workoutName)
+ return this.workoutList[index];
+ }
+ return (WorkoutPlaylist)null;
+ }
+ }
+}
diff --git a/BOXVR Playlist Manager/Helpers/NotifyingObject.cs b/BOXVR Playlist Manager/Helpers/NotifyingObject.cs
new file mode 100644
index 0000000..ada419b
--- /dev/null
+++ b/BOXVR Playlist Manager/Helpers/NotifyingObject.cs
@@ -0,0 +1,65 @@
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using System.Windows.Threading;
+
+namespace BoxVR_Playlist_Manager.Helpers
+{
+ public class NotifyingObject : INotifyPropertyChanged
+ {
+ protected Dispatcher _dispatcher;
+ public NotifyingObject(Dispatcher dispatcher)
+ {
+ _dispatcher = dispatcher;
+ }
+
+ ///
+ /// Multicast event for property change notifications.
+ ///
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ ///
+ /// Checks if a property already matches a desired value. Sets the property and
+ /// notifies listeners only when necessary.
+ ///
+ /// Type of the property.
+ /// Reference to a property with both getter and setter.
+ /// Desired value for the property.
+ ///
+ /// Name of the property used to notify listeners. This
+ /// value is optional and can be provided automatically when invoked from compilers that
+ /// support CallerMemberName.
+ ///
+ ///
+ /// True if the value was changed, false if the existing value matched the
+ /// desired value.
+ ///
+ protected bool SetProperty(ref T storage, T value, [CallerMemberName] string propertyName = null)
+ {
+ if(Equals(storage, value))
+ {
+ return false;
+ }
+
+ storage = value;
+ _dispatcher.Invoke(() => this.OnPropertyChanged(propertyName));
+ return true;
+ }
+
+ ///
+ /// Notifies listeners that a property value has changed.
+ ///
+ ///
+ /// Name of the property used to notify listeners. This
+ /// value is optional and can be provided automatically when invoked from compilers
+ /// that support .
+ ///
+ protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ PropertyChangedEventHandler eventHandler = this.PropertyChanged;
+ if(eventHandler != null)
+ {
+ eventHandler(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+ }
+}
diff --git a/BOXVR Playlist Manager/Helpers/Paths.cs b/BOXVR Playlist Manager/Helpers/Paths.cs
new file mode 100644
index 0000000..a31f012
--- /dev/null
+++ b/BOXVR Playlist Manager/Helpers/Paths.cs
@@ -0,0 +1,93 @@
+using System;
+using System.IO;
+using BoxVR_Playlist_Manager.FitXr.Enums;
+
+namespace BoxVR_Playlist_Manager.Helpers
+{
+ public class Paths
+ {
+ private static string _applicationPath => Environment.ExpandEnvironmentVariables(Properties.Settings.Default.BoxVRExePath);
+ private static string _persistentDataPath => Environment.ExpandEnvironmentVariables(Properties.Settings.Default.BoxVRAppDataPath);
+
+ public static string StreamingAssetsPath => Path.Combine(_applicationPath, "BoxVR_Data", "StreamingAssets");
+
+ public static string PersistentDataPath => _persistentDataPath;
+
+ public static string ApplicationPath => _applicationPath;
+
+ public static string RootDataFolder(LocationMode locationMode)
+ {
+ string str = "";
+ switch(locationMode)
+ {
+ case LocationMode.PlayerData:
+ str = _persistentDataPath + "/Playlists";
+ break;
+ case LocationMode.Workouts:
+ case LocationMode.Downloadable:
+ case LocationMode.MyWorkout:
+ App.logger.Error("No root path for these locations");
+ break;
+ case LocationMode.Editor:
+ str = _persistentDataPath + "/WorkoutEditor";
+ break;
+ }
+ return str.Replace("/", "\\");
+ }
+
+
+ public static string WorkoutDefinitionFolder(LocationMode locationMode, Game gameType)
+ {
+ string str = "";
+ switch(locationMode)
+ {
+ case LocationMode.PlayerData:
+ case LocationMode.Editor:
+ str = Paths.RootDataFolder(locationMode) + "/WorkoutPlaylists/" + gameType.ToString();
+ break;
+ case LocationMode.Workouts:
+ str = "WorkoutData/WorkoutPlaylists/" + gameType.ToString();
+ break;
+ }
+ return str.Replace("/", "\\");
+ }
+
+ public static string TrackDataFolder(LocationMode locationMode)
+ {
+ string str = "";
+ switch(locationMode)
+ {
+ case LocationMode.PlayerData:
+ case LocationMode.Editor:
+ str = Paths.RootDataFolder(locationMode) + "/TrackData/";
+ break;
+ case LocationMode.Workouts:
+ case LocationMode.MyWorkout:
+ str = "WorkoutData/TrackData/";
+ break;
+ }
+ return str.Replace("/", "\\");
+ }
+
+ public static string WavDataFolder(LocationMode locationMode) => Paths.TrackDataFolder(locationMode);
+
+ public static string ImgDataFolder(LocationMode locationMode) => Paths.TrackDataFolder(locationMode);
+
+ public static string DefinitionsFolder(LocationMode locationMode)
+ {
+ string str = "";
+ switch(locationMode)
+ {
+ case LocationMode.PlayerData:
+ case LocationMode.Editor:
+ str = Paths.RootDataFolder(locationMode) + "/TrackDefinitions/";
+ break;
+ case LocationMode.Workouts:
+ case LocationMode.MyWorkout:
+ str = "WorkoutData/TrackDefinitions/";
+ break;
+ }
+ return str.Replace("/", "\\");
+ }
+ }
+}
diff --git a/BOXVR Playlist Manager/IsEnabledColorConverter.cs b/BOXVR Playlist Manager/IsEnabledColorConverter.cs
index 02b20c6..1d69a23 100644
--- a/BOXVR Playlist Manager/IsEnabledColorConverter.cs
+++ b/BOXVR Playlist Manager/IsEnabledColorConverter.cs
@@ -1,9 +1,5 @@
using System;
-using System.Collections.Generic;
using System.Globalization;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using System.Windows.Data;
namespace BoxVR_Playlist_Manager
diff --git a/BOXVR Playlist Manager/MainWindow.xaml b/BOXVR Playlist Manager/MainWindow.xaml
index 57bbd11..78bdca6 100644
--- a/BOXVR Playlist Manager/MainWindow.xaml
+++ b/BOXVR Playlist Manager/MainWindow.xaml
@@ -5,7 +5,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BoxVR_Playlist_Manager"
mc:Ignorable="d"
- Title="BOXVR Playlist Manager" Height="450" Width="800">
+ Title="BOXVR Playlist Manager" Height="450" Width="1000">
-
M444.788 291.1l42.616 24.599c4.867 2.809 7.126 8.618 5.459 13.985-11.07 35.642-29.97 67.842-54.689 94.586a12.016 12.016 0 0 1-14.832 2.254l-42.584-24.595a191.577 191.577 0 0 1-60.759 35.13v49.182a12.01 12.01 0 0 1-9.377 11.718c-34.956 7.85-72.499 8.256-109.219.007-5.49-1.233-9.403-6.096-9.403-11.723v-49.184a191.555 191.555 0 0 1-60.759-35.13l-42.584 24.595a12.016 12.016 0 0 1-14.832-2.254c-24.718-26.744-43.619-58.944-54.689-94.586-1.667-5.366.592-11.175 5.459-13.985L67.212 291.1a193.48 193.48 0 0 1 0-70.199l-42.616-24.599c-4.867-2.809-7.126-8.618-5.459-13.985 11.07-35.642 29.97-67.842 54.689-94.586a12.016 12.016 0 0 1 14.832-2.254l42.584 24.595a191.577 191.577 0 0 1 60.759-35.13V25.759a12.01 12.01 0 0 1 9.377-11.718c34.956-7.85 72.499-8.256 109.219-.007 5.49 1.233 9.403 6.096 9.403 11.723v49.184a191.555 191.555 0 0 1 60.759 35.13l42.584-24.595a12.016 12.016 0 0 1 14.832 2.254c24.718 26.744 43.619 58.944 54.689 94.586 1.667 5.366-.592 11.175-5.459 13.985L444.788 220.9a193.485 193.485 0 0 1 0 70.2zM336 256c0-44.112-35.888-80-80-80s-80 35.888-80 80 35.888 80 80 80 80-35.888 80-80z
@@ -35,8 +34,9 @@
M413.1 222.5l22.2 22.2c9.4 9.4 9.4 24.6 0 33.9L241 473c-9.4 9.4-24.6 9.4-33.9 0L12.7 278.6c-9.4-9.4-9.4-24.6 0-33.9l22.2-22.2c9.5-9.5 25-9.3 34.3.4L184 343.4V56c0-13.3 10.7-24 24-24h32c13.3 0 24 10.7 24 24v287.4l114.8-120.5c9.3-9.8 24.8-10 34.3-.4z
M16 288c-8.84 0-16 7.16-16 16v32c0 8.84 7.16 16 16 16h112v-64H16zm336-152V0H152c-13.3 0-24 10.7-24 24v264h127.99v-65.18c0-14.28 17.29-21.41 27.36-11.27l95.7 96.43c6.6 6.65 6.6 17.39 0 24.04l-95.7 96.42c-10.06 10.14-27.36 3.01-27.36-11.27V352H128v136c0 13.3 10.7 24 24 24h336c13.3 0 24-10.7 24-24V160H376c-13.2 0-24-10.8-24-24zm153-31L407.1 7c-4.5-4.5-10.6-7-17-7H384v128h128v-6.1c0-6.3-2.5-12.4-7-16.9z
M0 84V56c0-13.3 10.7-24 24-24h112l9.4-18.7c4-8.2 12.3-13.3 21.4-13.3h114.3c9.1 0 17.4 5.1 21.5 13.3L312 32h112c13.3 0 24 10.7 24 24v28c0 6.6-5.4 12-12 12H12C5.4 96 0 90.6 0 84zm416 56v324c0 26.5-21.5 48-48 48H80c-26.5 0-48-21.5-48-48V140c0-6.6 5.4-12 12-12h360c6.6 0 12 5.4 12 12zm-272 68c0-8.8-7.2-16-16-16s-16 7.2-16 16v224c0 8.8 7.2 16 16 16s16-7.2 16-16V208zm96 0c0-8.8-7.2-16-16-16s-16 7.2-16 16v224c0 8.8 7.2 16 16 16s16-7.2 16-16V208zm96 0c0-8.8-7.2-16-16-16s-16 7.2-16 16v224c0 8.8 7.2 16 16 16s16-7.2 16-16V208z
+
-
+
@@ -63,12 +63,12 @@
-
-
-
+
+
+
+
+
-
+
@@ -90,16 +96,16 @@
-
-
+
+
-
+
-
-
+
+
-
+
@@ -108,7 +114,7 @@
-
+
@@ -121,17 +127,17 @@
-
+
-
+
+ IsEnabled="{Binding IsModified}" Margin="4,0" Command="{Binding SavePlaylistCommand}">
+ Margin="4,0" Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=DataContext.RemovePlaylistCommand}">
@@ -139,38 +145,45 @@
-
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -179,7 +192,7 @@
-
+
@@ -194,8 +207,21 @@
-
- Please wait, generating beatmaps...
+
+
+
+
+
+
+ Please wait, generating beatmaps...
+
+
+
+
+
+
+
+ Please setup paths in the settings
diff --git a/BOXVR Playlist Manager/MainWindow.xaml.cs b/BOXVR Playlist Manager/MainWindow.xaml.cs
index 8229fe2..a0e3f5d 100644
--- a/BOXVR Playlist Manager/MainWindow.xaml.cs
+++ b/BOXVR Playlist Manager/MainWindow.xaml.cs
@@ -1,20 +1,6 @@
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.Collections.ObjectModel;
using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Data;
-using System.Windows.Documents;
-using System.Windows.Input;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using System.Windows.Navigation;
-using System.Windows.Shapes;
+using NLog;
namespace BoxVR_Playlist_Manager
{
@@ -23,326 +9,14 @@ namespace BoxVR_Playlist_Manager
///
public partial class MainWindow : Window
{
- private ObservableCollection Playlists;
-
- private Playlist _selectedPlaylist;
- private Playlist SelectedPlaylist
- {
- get => _selectedPlaylist;
- set
- {
- _selectedPlaylist = value;
- playlistView.DataContext = SelectedPlaylist;
- gridGeneratingBeatmaps.DataContext = SelectedPlaylist;
- }
- }
-
- private delegate Point GetPosition(IInputElement element);
- private int _rowIndex;
+ private Logger _log = LogManager.GetLogger(nameof(MainWindow));
public MainWindow()
{
- App.logger.Trace("MainWindow initializing");
+ _log.Debug("MainWindow initializing");
InitializeComponent();
-
- _rowIndex = -1;
- playlistTracks.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(playlistTracks_PreviewMouseLeftButtonDown);
- playlistTracks.Drop += new DragEventHandler(playlistTracks_Drop);
- App.logger.Trace("MainWindow initialized");
-
- LoadPlaylists();
- icPlaylists.ItemsSource = icCtxCopyTo.ItemsSource = icCtxMoveTo.ItemsSource = Playlists;
- }
-
- private void playlistTracks_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
- {
- _rowIndex = GetCurrentRowIndex(e.GetPosition);
- if (_rowIndex < 0)
- return;
-
- playlistTracks.SelectedIndex = _rowIndex;
- if (!(playlistTracks.Items[_rowIndex] is Track track))
- return;
-
- if (DragDrop.DoDragDrop(playlistTracks, track, DragDropEffects.Move) != DragDropEffects.None)
- playlistTracks.SelectedItem = track;
- }
-
- private void playlistTracks_Drop(object sender, DragEventArgs e)
- {
- if (_rowIndex < 0)
- return;
-
- var i = GetCurrentRowIndex(e.GetPosition);
-
- if (i < 0 || i == _rowIndex)
- return;
-
- var movedTrack = SelectedPlaylist.Tracks[_rowIndex];
- // if this is the last row, add to end, else in position
- if (i == playlistTracks.Items.Count - 1)
- {
- SelectedPlaylist.Tracks.RemoveAt(_rowIndex);
- SelectedPlaylist.Tracks.Add(movedTrack);
- }
- else
- {
- SelectedPlaylist.Tracks.RemoveAt(_rowIndex);
- SelectedPlaylist.Tracks.Insert(i, movedTrack);
- }
- }
-
- private int GetCurrentRowIndex(GetPosition pos)
- {
- for(int i = 0; i < playlistTracks.Items.Count; i++)
- {
- var item = GetRowItem(i);
- if (GetMouseTargetRow(item, pos))
- return i;
- }
-
- return -1;
- }
-
- private DataGridRow GetRowItem(int index)
- {
- if (playlistTracks.ItemContainerGenerator.Status != System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
- return null;
-
- return playlistTracks.ItemContainerGenerator.ContainerFromIndex(index) as DataGridRow;
- }
-
- private bool GetMouseTargetRow(Visual target, GetPosition position)
- {
- if (target == null) return false;
- var rect = VisualTreeHelper.GetDescendantBounds(target);
- var point = position((IInputElement)target);
- return rect.Contains(point);
- }
-
- private void settingsButton_Click(object sender, RoutedEventArgs e)
- {
- App.logger.Trace("Settings window opened");
- var settingsWindow = new SettingsWindow();
- var settingsChanged = settingsWindow.ShowDialog();
- if(settingsChanged.HasValue && settingsChanged.Value)
- {
- App.logger.Trace("Settings were changed");
- LoadPlaylists();
- }
- else
- {
- App.logger.Trace("No settings changed");
- }
- }
-
- private void playlistItem_Click(object sender, RoutedEventArgs e)
- {
- var newPlaylist = ((Button)sender).Tag as Playlist;
- if (SelectedPlaylist != null && newPlaylist != null && SelectedPlaylist != newPlaylist && SelectedPlaylist.IsModified)
- {
- var result = MessageBox.Show("Do you want to save your changes?", "Save Changes", MessageBoxButton.YesNoCancel, MessageBoxImage.Question, MessageBoxResult.Yes);
- if(result == MessageBoxResult.Yes)
- {
- SelectedPlaylist.Save().ContinueWith(t => Dispatcher.Invoke(() => SelectedPlaylist = ((Button)sender).Tag as Playlist));
- }
- else if(result == MessageBoxResult.No)
- {
- SelectedPlaylist.Reset();
- SelectedPlaylist = ((Button)sender).Tag as Playlist;
- }
- }
- else
- {
- SelectedPlaylist = ((Button)sender).Tag as Playlist;
- }
-
- }
-
- private void ctx_Remove_Click(object sender, RoutedEventArgs e)
- {
- if (SelectedPlaylist.SelectedTrack != null)
- SelectedPlaylist.Tracks.Remove(SelectedPlaylist.SelectedTrack);
- }
-
- private void ctx_MoveTo_Click(object sender, RoutedEventArgs e)
- {
- if (((MenuItem)sender).Tag is Playlist moveToPlaylist && SelectedPlaylist.SelectedTrack != null)
- {
- moveToPlaylist.Tracks.Add(SelectedPlaylist.SelectedTrack);
- SelectedPlaylist.Tracks.Remove(SelectedPlaylist.SelectedTrack);
- }
-
- }
-
- private void ctx_CopyTo_Click(object sender, RoutedEventArgs e)
- {
- if (((MenuItem)sender).Tag is Playlist copyToPlaylist && SelectedPlaylist.SelectedTrack != null)
- copyToPlaylist.Tracks.Add(SelectedPlaylist.SelectedTrack);
- }
-
- private void ctx_Explorer_Click(object sender, RoutedEventArgs e)
- {
- if(SelectedPlaylist.SelectedTrack != null)
- {
- using (var p = new Process())
- {
- p.StartInfo = new ProcessStartInfo()
- {
- FileName = SelectedPlaylist.SelectedTrack.Path,
- UseShellExecute = true
- };
- p.Start();
- }
- }
-
- }
-
- private void trackMoveUp_Click(object sender, RoutedEventArgs e)
- {
- var selectedTrack = ((Button)sender).Tag as Track;
- var index = SelectedPlaylist.Tracks.IndexOf(selectedTrack);
- if (index > 0)
- {
- var prev = SelectedPlaylist.Tracks[index - 1];
- SelectedPlaylist.Tracks[index - 1] = selectedTrack;
- SelectedPlaylist.Tracks[index] = prev;
- }
- }
-
- private void trackMoveDown_Click(object sender, RoutedEventArgs e)
- {
- var selectedTrack = ((Button)sender).Tag as Track;
- var index = SelectedPlaylist.Tracks.IndexOf(selectedTrack);
- if(index < SelectedPlaylist.Tracks.Count - 1)
- {
- var next = SelectedPlaylist.Tracks[index + 1];
- SelectedPlaylist.Tracks[index + 1] = selectedTrack;
- SelectedPlaylist.Tracks[index] = next;
- }
- }
-
- private void LoadPlaylists()
- {
- App.logger.Trace("Playlists loading");
- Playlists = new ObservableCollection();
- var playlistPath = System.IO.Path.Combine(Environment.ExpandEnvironmentVariables(Properties.Settings.Default.BoxVRAppDataPath), "Playlists");
-
- if (Directory.Exists(playlistPath))
- {
- foreach(var playlist in Directory.EnumerateFiles(playlistPath, "*.playlist.txt"))
- {
- Playlists.Add(new Playlist(playlist));
- }
- }
- else
- {
- App.logger.Debug($"Playlist path does not exist: {playlistPath}");
- return;
- }
-
- App.logger.Trace($"{Playlists.Count} playlists loaded from {playlistPath}");
- }
-
- private void btnAddPlaylist_Click(object sender, RoutedEventArgs e)
- {
- var p = new Playlist();
- Playlists.Add(p);
- SelectedPlaylist = p;
- }
-
- private void btnSavePlaylist_Click(object sender, RoutedEventArgs e)
- {
- if (SelectedPlaylist != null)
- Task.Run(async () => await SelectedPlaylist.Save());
- }
-
- private void btnImportPlaylist_Click(object sender, RoutedEventArgs e)
- {
- using(var dlg = new System.Windows.Forms.OpenFileDialog())
- {
- dlg.AutoUpgradeEnabled = true;
- dlg.CheckFileExists = true;
- dlg.CheckPathExists = true;
- dlg.Multiselect = true;
- dlg.Title = "Import Playlist";
- dlg.Filter = Playlist.FileDialogFilter;
- var result = dlg.ShowDialog();
-
- if(result == System.Windows.Forms.DialogResult.OK)
- {
- foreach (var file in dlg.FileNames)
- {
- var playlist = Task.Run(async () => await Playlist.Import(file)).Result;
- Playlists.Add(playlist);
- SelectedPlaylist = playlist;
- }
- }
- }
- }
-
- private void btnAddTrack_Click(object sender, RoutedEventArgs e)
- {
- if(SelectedPlaylist != null)
- {
- using (var dlg = new System.Windows.Forms.OpenFileDialog())
- {
- dlg.AutoUpgradeEnabled = true;
- dlg.CheckFileExists = true;
- dlg.CheckPathExists = true;
- dlg.Multiselect = true;
- dlg.Title = "Add Track";
- dlg.Filter = Track.FileDialogFilter;
- var result = dlg.ShowDialog();
-
- if (result == System.Windows.Forms.DialogResult.OK)
- {
- foreach (var file in dlg.FileNames)
- SelectedPlaylist.Tracks.Add(new Track(file));
- }
- }
- }
- }
-
- private void playlistTracks_Sorting(object sender, DataGridSortingEventArgs e)
- {
- if(SelectedPlaylist != null)
- {
- var prop = typeof(Track).GetProperty(e.Column.SortMemberPath);
- if(prop != null)
- {
- var trackList = new List
diff --git a/BOXVR Playlist Manager/Track.cs b/BOXVR Playlist Manager/Track.cs
deleted file mode 100644
index 0736791..0000000
--- a/BOXVR Playlist Manager/Track.cs
+++ /dev/null
@@ -1,384 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace BoxVR_Playlist_Manager
-{
- public class Track : EqualityComparer
- {
- const string STR_UNKNOWN = "";
-
- private ATL.Track _atlTrack;
-
- public string Path { get; private set; }
-
- public string Title => string.IsNullOrWhiteSpace(_atlTrack.Title) ? STR_UNKNOWN : _atlTrack.Title;
-
- public string Artist
- {
- get
- {
- var artist = _atlTrack.AlbumArtist;
- if (string.IsNullOrWhiteSpace(artist)) artist = _atlTrack.Artist;
- return string.IsNullOrWhiteSpace(artist) ? STR_UNKNOWN : artist;
- }
- }
-
- public string Album => string.IsNullOrWhiteSpace(_atlTrack.Album) ? STR_UNKNOWN : _atlTrack.Album;
-
- public int DurationInt => _atlTrack.Duration;
- public TimeSpan Duration => new TimeSpan(0, 0, DurationInt);
-
- public Track(string path)
- {
- Path = path;
- _atlTrack = new ATL.Track(path);
- }
-
- public override bool Equals(Track x, Track y)
- {
- if (ReferenceEquals(x, y))
- return true;
-
- if (x is null || y is null)
- return false;
-
- return x.Path.Equals(y.Path, StringComparison.OrdinalIgnoreCase);
- }
-
- public override int GetHashCode(Track obj) => base.GetHashCode();
-
- private static Dictionary _supportedTrackFormats;
- public static Dictionary SupportedTrackFormats
- {
- get
- {
- // this list pulled from ffmpeg version bundled with BoxVR on 15/11/18
- if (_supportedTrackFormats == null || !_supportedTrackFormats.Any())
- {
- _supportedTrackFormats = new Dictionary()
- {
- { "4X Technologies", "*.4xm" },
- { "Audible AA format files", "*.aa" },
- { "raw ADTS AAC (Advanced Audio Coding)", "*.aac" },
- { "raw AC-3", "*.ac3" },
- { "Interplay ACM", "*.acm" },
- { "ACT Voice file format", "*.act" },
- { "Artworx Data Format", "*.adf" },
- { "ADP", "*.adp" },
- { "Sony PS2 ADS", "*.ads" },
- { "CRI ADX", "*.adx" },
- { "MD STUDIO audio", "*.aea" },
- { "AFC", "*.afc" },
- { "Audio IFF", "*.aiff" },
- { "CRI AIX", "*.aix" },
- { "PCM A-law", "*.alaw" },
- { "Alias/Wavefront PIX image", "*.alias_pix" },
- { "3GPP AMR", "*.amr" },
- { "raw AMR-NB", "*.amrnb" },
- { "raw AMR-WB", "*.amrwb" },
- { "Deluxe Paint Animation", "*.anm" },
- { "CRYO APC", "*.apc" },
- { "Monkey's Audio", "*.ape" },
- { "Animated Portable Network Graphics", "*.apng" },
- { "raw aptX (Audio Processing Technology for Bluetooth)", "*.aptx" },
- { "raw aptX HD (Audio Processing Technology for Bluetooth)", "*.aptx_hd" },
- { "AQTitle subtitles", "*.aqtitle" },
- { "ASF (Advanced / Active Streaming Format)", "*.asf;*.asf_o" },
- { "SSA (SubStation Alpha) subtitle", "*.ass" },
- { "AST (Audio Stream)", "*.ast" },
- { "Sun AU", "*.au" },
- { "AVI (Audio Video Interleaved)", "*.avi" },
- { "AviSynth script", "*.avisynth" },
- { "AVR (Audio Visual Research)", "*.avr" },
- { "AVS", "*.avs" },
- { "Bethesda Softworks VID", "*.bethsoftvid" },
- { "Brute Force & Ignorance", "*.bfi" },
- { "BFSTM (Binary Cafe Stream)", "*.bfstm" },
- { "Binary text", "*.bin" },
- { "Bink", "*.bink" },
- { "G.729 BIT file format", "*.bit" },
- { "piped bmp sequence", "*.bmp_pipe" },
- { "Discworld II BMV", "*.bmv" },
- { "Black Ops Audio", "*.boa" },
- { "BRender PIX image", "*.brender_pix" },
- { "BRSTM (Binary Revolution Stream)", "*.brstm" },
- { "Interplay C93", "*.c93" },
- { "Apple CAF (Core Audio Format)", "*.caf" },
- { "raw Chinese AVS (Audio Video Standard) video", "*.cavsvideo" },
- { "CD Graphics", "*.cdg" },
- { "Commodore CDXL video", "*.cdxl" },
- { "Phantom Cine", "*.cine" },
- { "codec2 .c2 muxer", "*.codec2" },
- { "raw codec2 muxer", "*.codec2raw" },
- { "Virtual concatenation script", "*.concat" },
- { "DASH Muxer", "*.dash" },
- { "raw data", "*.data" },
- { "D-Cinema audio", "*.daud" },
- { "Sega DC STR", "*.dcstr" },
- { "piped dds sequence", "*.dds_pipe" },
- { "Chronomaster DFA", "*.dfa" },
- { "raw Dirac", "*.dirac" },
- { "raw DNxHD (SMPTE VC-3)", "*.dnxhd" },
- { "piped dpx sequence", "*.dpx_pipe" },
- { "DSD Stream File (DSF)", "*.dsf" },
- { "DirectShow capture", "*.dshow" },
- { "Delphine Software International CIN", "*.dsicin" },
- { "Digital Speech Standard (DSS)", "*.dss" },
- { "raw DTS", "*.dts" },
- { "raw DTS-HD", "*.dtshd" },
- { "DV (Digital Video)", "*.dv" },
- { "raw dvbsub", "*.dvbsub" },
- { "dvbtxt", "*.dvbtxt" },
- { "DXA", "*.dxa" },
- { "Electronic Arts Multimedia", "*.ea" },
- { "Electronic Arts cdata", "*.ea_cdata" },
- { "raw E-AC-3", "*.eac3" },
- { "Ensoniq Paris Audio File", "*.epaf" },
- { "piped exr sequence", "*.exr_pipe" },
- { "PCM 32-bit floating-point big-endian", "*.f32be" },
- { "PCM 32-bit floating-point little-endian", "*.f32le" },
- { "PCM 64-bit floating-point big-endian", "*.f64be" },
- { "PCM 64-bit floating-point little-endian", "*.f64le" },
- { "FFmpeg metadata in text", "*.ffmetadata" },
- { "Sega FILM / CPK", "*.film_cpk" },
- { "Adobe Filmstrip", "*.filmstrip" },
- { "Flexible Image Transport System", "*.fits" },
- { "raw FLAC", "*.flac" },
- { "FLI/FLC/FLX animation", "*.flic" },
- { "FLV (Flash Video)", "*.flv" },
- { "Megalux Frame", "*.frm" },
- { "FMOD Sample Bank", "*.fsb" },
- { "raw G.722", "*.g722" },
- { "raw G.723.1", "*.g723_1" },
- { "raw big-endian G.726 (\"left-justified\")", "*.g726" },
- { "raw little-endian G.726 (\"right-justified\")", "*.g726le" },
- { "G.729 raw format demuxer", "*.g729" },
- { "GDI API Windows frame grabber", "*.gdigrab" },
- { "Gremlin Digital Video", "*.gdv" },
- { "GENeric Header", "*.genh" },
- { "GIF Animation", "*.gif" },
- { "raw GSM", "*.gsm" },
- { "GXF (General eXchange Format)", "*.gxf" },
- { "raw H.261", "*.h261" },
- { "raw H.263", "*.h263" },
- { "raw H.264 video", "*.h264" },
- { "raw HEVC video", "*.hevc" },
- { "Apple HTTP Live Streaming", "*.hls;*.applehttp" },
- { "Cryo HNM v4", "*.hnm" },
- { "Microsoft Windows ICO", "*.ico" },
- { "id Cinematic", "*.idcin" },
- { "iCE Draw File", "*.idf" },
- { "IFF (Interchange File Format)", "*.iff" },
- { "iLBC storage", "*.ilbc" },
- { "image2 sequence", "*.image2" },
- { "piped image2 sequence", "*.image2pipe" },
- { "raw Ingenient MJPEG", "*.ingenient" },
- { "Interplay MVE", "*.ipmovie" },
- { "Berkeley/IRCAM/CARL Sound Format", "*.ircam" },
- { "Funcom ISS", "*.iss" },
- { "IndigoVision 8000 video", "*.iv8" },
- { "On2 IVF", "*.ivf" },
- { "IVR (Internet Video Recording)", "*.ivr" },
- { "piped j2k sequence", "*.j2k_pipe" },
- { "JACOsub subtitle format", "*.jacosub" },
- { "piped jpeg sequence", "*.jpeg_pipe" },
- { "piped jpegls sequence", "*.jpegls_pipe" },
- { "Bitmap Brothers JV", "*.jv" },
- { "Libavfilter virtual input device", "*.lavfi" },
- { "live RTMP FLV (Flash Video)", "*.live_flv" },
- { "raw lmlm4", "*.lmlm4" },
- { "LOAS AudioSyncStream", "*.loas" },
- { "LRC lyrics", "*.lrc" },
- { "LVF", "*.lvf" },
- { "VR native stream (LXF)", "*.lxf" },
- { "raw MPEG-4 video", "*.m4v" },
- { "Matroska / WebM", "*.matroska;*.webm" },
- { "Metal Gear Solid: The Twin Snakes", "*.mgsts" },
- { "MicroDVD subtitle format", "*.microdvd" },
- { "raw MJPEG video", "*.mjpeg" },
- { "raw MJPEG 2000 video", "*.mjpeg_2000" },
- { "raw MLP", "*.mlp" },
- { "Magic Lantern Video (MLV)", "*.mlv" },
- { "American Laser Games MM", "*.mm" },
- { "Yamaha SMAF", "*.mmf" },
- { "QuickTime / MOV", "*.mov;*.mp4;*.m4a;*.3gp;*.3g2;*.mj2" },
- { "MP3 (MPEG audio layer 3)", "*.mp3" },
- { "Musepack", "*.mpc" },
- { "Musepack SV8", "*.mpc8" },
- { "MPEG-1 Systems / MPEG program stream", "*.mpeg" },
- { "MPEG-TS (MPEG-2 Transport Stream)", "*.mpegts" },
- { "raw MPEG-TS (MPEG-2 Transport Stream)", "*.mpegtsraw" },
- { "raw MPEG video", "*.mpegvideo" },
- { "MIME multipart JPEG", "*.mpjpeg" },
- { "MPL2 subtitles", "*.mpl2" },
- { "MPlayer subtitles", "*.mpsub" },
- { "Sony PS3 MSF", "*.msf" },
- { "MSN TCP Webcam stream", "*.msnwctcp" },
- { "Konami PS2 MTAF", "*.mtaf" },
- { "MTV", "*.mtv" },
- { "PCM mu-law", "*.mulaw" },
- { "Eurocom MUSX", "*.musx" },
- { "Silicon Graphics Movie", "*.mv" },
- { "Motion Pixels MVI", "*.mvi" },
- { "MXF (Material eXchange Format)", "*.mxf" },
- { "MxPEG clip", "*.mxg" },
- { "NC camera feed", "*.nc" },
- { "NIST SPeech HEader REsources", "*.nistsphere" },
- { "Computerized Speech Lab NSP", "*.nsp" },
- { "Nullsoft Streaming Video", "*.nsv" },
- { "NUT", "*.nut" },
- { "NuppelVideo", "*.nuv" },
- { "Ogg", "*.ogg" },
- { "Sony OpenMG audio", "*.oma" },
- { "Amazing Studio Packed Animation File", "*.paf" },
- { "piped pam sequence", "*.pam_pipe" },
- { "piped pbm sequence", "*.pbm_pipe" },
- { "piped pcx sequence", "*.pcx_pipe" },
- { "piped pgm sequence", "*.pgm_pipe" },
- { "piped pgmyuv sequence", "*.pgmyuv_pipe" },
- { "piped pictor sequence", "*.pictor_pipe" },
- { "PJS (Phoenix Japanimation Society) subtitles", "*.pjs" },
- { "Playstation Portable PMP", "*.pmp" },
- { "piped png sequence", "*.png_pipe" },
- { "piped ppm sequence", "*.ppm_pipe" },
- { "piped psd sequence", "*.psd_pipe" },
- { "Sony Playstation STR", "*.psxstr" },
- { "TechnoTrend PVA", "*.pva" },
- { "PVF (Portable Voice Format)", "*.pvf" },
- { "QCP", "*.qcp" },
- { "piped qdraw sequence", "*.qdraw_pipe" },
- { "REDCODE R3D", "*.r3d" },
- { "raw video", "*.rawvideo" },
- { "RealText subtitle format", "*.realtext" },
- { "RedSpark", "*.redspark" },
- { "RL2", "*.rl2" },
- { "RealMedia", "*.rm" },
- { "raw id RoQ", "*.roq" },
- { "RPL / ARMovie", "*.rpl" },
- { "GameCube RSD", "*.rsd" },
- { "Lego Mindstorms RSO", "*.rso" },
- { "RTP output", "*.rtp" },
- { "RTSP output", "*.rtsp" },
- { "PCM signed 16-bit big-endian", "*.s16be" },
- { "PCM signed 16-bit little-endian", "*.s16le" },
- { "PCM signed 24-bit big-endian", "*.s24be" },
- { "PCM signed 24-bit little-endian", "*.s24le" },
- { "PCM signed 32-bit big-endian", "*.s32be" },
- { "PCM signed 32-bit little-endian", "*.s32le" },
- { "SMPTE 337M", "*.s337m" },
- { "PCM signed 8-bit", "*.s8" },
- { "SAMI subtitle format", "*.sami" },
- { "SAP output", "*.sap" },
- { "raw SBC", "*.sbc" },
- { "SBaGen binaural beats script", "*.sbg" },
- { "Scenarist Closed Captions", "*.scc" },
- { "SDP", "*.sdp" },
- { "SDR2", "*.sdr2" },
- { "MIDI Sample Dump Standard", "*.sds" },
- { "Sample Dump eXchange", "*.sdx" },
- { "piped sgi sequence", "*.sgi_pipe" },
- { "raw Shorten", "*.shn" },
- { "Beam Software SIFF", "*.siff" },
- { "Asterisk raw pcm", "*.sln" },
- { "Loki SDL MJPEG", "*.smjpeg" },
- { "Smacker", "*.smk" },
- { "LucasArts Smush", "*.smush" },
- { "Sierra SOL", "*.sol" },
- { "SoX native", "*.sox" },
- { "IEC 61937 (used on S/PDIF - IEC958)", "*.spdif" },
- { "SubRip subtitle", "*.srt" },
- { "Spruce subtitle format", "*.stl" },
- { "SubViewer subtitle format", "*.subviewer" },
- { "SubViewer v1 subtitle format", "*.subviewer1" },
- { "piped sunrast sequence", "*.sunrast_pipe" },
- { "raw HDMV Presentation Graphic Stream subtitles", "*.sup" },
- { "Konami PS2 SVAG", "*.svag" },
- { "piped svg sequence", "*.svg_pipe" },
- { "SWF (ShockWave Flash)", "*.swf" },
- { "raw TAK", "*.tak" },
- { "TED Talks captions", "*.tedcaptions" },
- { "THP", "*.thp" },
- { "Tiertex Limited SEQ", "*.tiertexseq" },
- { "piped tiff sequence", "*.tiff_pipe" },
- { "8088flex TMV", "*.tmv" },
- { "raw TrueHD", "*.truehd" },
- { "TTA (True Audio)", "*.tta" },
- { "Tele-typewriter", "*.tty" },
- { "Renderware TeXture Dictionary", "*.txd" },
- { "TiVo TY Stream", "*.ty" },
- { "PCM unsigned 16-bit big-endian", "*.u16be" },
- { "PCM unsigned 16-bit little-endian", "*.u16le" },
- { "PCM unsigned 24-bit big-endian", "*.u24be" },
- { "PCM unsigned 24-bit little-endian", "*.u24le" },
- { "PCM unsigned 32-bit big-endian", "*.u32be" },
- { "PCM unsigned 32-bit little-endian", "*.u32le" },
- { "PCM unsigned 8-bit", "*.u8" },
- { "Uncompressed 4:2:2 10-bit", "*.v210;*.v201x" },
- { "Sony PS2 VAG", "*.vag" },
- { "raw VC-1 video", "*.vc1" },
- { "VC-1 test bitstream", "*.vc1test" },
- { "VfW video capture", "*.vfwcap" },
- { "Vivo", "*.vivo" },
- { "Sierra VMD", "*.vmd" },
- { "VobSub subtitle format", "*.vobsub" },
- { "Creative Voice", "*.voc" },
- { "Sony PS2 VPK", "*.vpk" },
- { "VPlayer subtitles", "*.vplayer" },
- { "Nippon Telegraph and Telephone Corporation (NTT) TwinVQ", "*.vqf" },
- { "Sony Wave64", "*.w64" },
- { "WAV / WAVE (Waveform Audio)", "*.wav" },
- { "Wing Commander III movie", "*.wc3movie" },
- { "WebM DASH Manifest", "*.webm_dash_manifest" },
- { "piped webp sequence", "*.webp_pipe" },
- { "WebVTT subtitle", "*.webvtt" },
- { "Westwood Studios audio", "*.wsaud" },
- { "Wideband Single-bit Data (WSD)", "*.wsd" },
- { "Westwood Studios VQA", "*.wsvqa" },
- { "Windows Television (WTV)", "*.wtv" },
- { "raw WavPack", "*.wv" },
- { "Psion 3 audio", "*.wve" },
- { "Maxis XA", "*.xa" },
- { "eXtended BINary text (XBIN)", "*.xbin" },
- { "Microsoft XMV", "*.xmv" },
- { "piped xpm sequence", "*.xpm_pipe" },
- { "Sony PS3 XVAG", "*.xvag" },
- { "Microsoft xWMA", "*.xwma" },
- { "Psygnosis YOP", "*.yop" },
- { "YUV4MPEG pipe", "*.yuv4mpegpipe" },
-
- };
- }
-
- return _supportedTrackFormats;
- }
- }
-
- private static string _fileDialogFilter;
- public static string FileDialogFilter
- {
- get
- {
- if (string.IsNullOrEmpty(_fileDialogFilter))
- {
- var allFiletypes = new List();
- foreach (var format in SupportedTrackFormats)
- allFiletypes.Add(format.Value);
-
- var filter = new StringBuilder();
- filter.Append($"All supported files|{string.Join(";", allFiletypes)}");
-
- foreach (var format in SupportedTrackFormats)
- filter.Append($"|{format.Key}|{format.Value}");
-
- _fileDialogFilter = filter.ToString();
- }
-
- return _fileDialogFilter;
- }
- }
- }
-}
diff --git a/BOXVR Playlist Manager/beatmap.bat b/BOXVR Playlist Manager/beatmap.bat
deleted file mode 100644
index 1f4e7ae..0000000
--- a/BOXVR Playlist Manager/beatmap.bat
+++ /dev/null
@@ -1,20 +0,0 @@
-SET BOXVR=%1
-SET BOXVR=%BOXVR:"=%
-
-SET BOXVR_DATA=%2
-SET BOXVR_DATA=%BOXVR_DATA:"=%
-
-SET S_PATH=%3
-SET S_PATH=%S_PATH:"=%
-
-SET S_NAME=%4
-SET S_NAME=%S_NAME:"=%
-
-SET S_EXT=%5
-SET S_EXT=%S_EXT:"=%
-
-REM Step 1: Convert to WAV
-IF NOT EXIST "%BOXVR_DATA%\%S_NAME%.wav" "%BOXVR%\BOXVR_Data\StreamingAssets\ffmpeg\bin\ffmpeg.exe" -y -i "%S_PATH%\%S_NAME%%S_EXT%" -ar 44100 "%BOXVR_DATA%\%S_NAME%.wav"
-
-REM Step 2: Track beats
-IF NOT EXIST "%BOXVR_DATA%\%S_NAME%.madmom.txt" "%BOXVR%\BOXVR_Data\StreamingAssets\DBNDownBeatTracker\DBNDownBeatTracker.exe" --beats_per_bar 4 single "%BOXVR_DATA%\%S_NAME%.wav" -o "%BOXVR_DATA%\%S_NAME%.madmom.txt" -j 1
\ No newline at end of file
diff --git a/BOXVR Playlist Manager/packages.config b/BOXVR Playlist Manager/packages.config
index 699478e..9ecbfc9 100644
--- a/BOXVR Playlist Manager/packages.config
+++ b/BOXVR Playlist Manager/packages.config
@@ -1,7 +1,9 @@
+
+
\ No newline at end of file
diff --git a/BOXVRPlaylistManager.sln b/BOXVRPlaylistManager.sln
index d231dee..2715f40 100644
--- a/BOXVRPlaylistManager.sln
+++ b/BOXVRPlaylistManager.sln
@@ -1,10 +1,14 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.28010.2048
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.31112.23
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BOXVR Playlist Manager", "BOXVR Playlist Manager\BOXVR Playlist Manager.csproj", "{7384CEB2-7BFF-4127-BF84-A145C3422CED}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BoxVRPlaylistManagerNETCore", "BoxVRPlaylistManagerNETCore\BoxVRPlaylistManagerNETCore.csproj", "{CDB99F3A-0C5F-4E4B-9A2C-57340389B0B5}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpotiSharp", "..\SpotiSharp\SpotiSharp.csproj", "{AFB48153-F9A2-49C2-9BE8-2F1F8EAD058D}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -18,6 +22,18 @@ Global
{7384CEB2-7BFF-4127-BF84-A145C3422CED}.Publish|Any CPU.Build.0 = Publish|Any CPU
{7384CEB2-7BFF-4127-BF84-A145C3422CED}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7384CEB2-7BFF-4127-BF84-A145C3422CED}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CDB99F3A-0C5F-4E4B-9A2C-57340389B0B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CDB99F3A-0C5F-4E4B-9A2C-57340389B0B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CDB99F3A-0C5F-4E4B-9A2C-57340389B0B5}.Publish|Any CPU.ActiveCfg = Debug|Any CPU
+ {CDB99F3A-0C5F-4E4B-9A2C-57340389B0B5}.Publish|Any CPU.Build.0 = Debug|Any CPU
+ {CDB99F3A-0C5F-4E4B-9A2C-57340389B0B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CDB99F3A-0C5F-4E4B-9A2C-57340389B0B5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AFB48153-F9A2-49C2-9BE8-2F1F8EAD058D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AFB48153-F9A2-49C2-9BE8-2F1F8EAD058D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AFB48153-F9A2-49C2-9BE8-2F1F8EAD058D}.Publish|Any CPU.ActiveCfg = Debug|Any CPU
+ {AFB48153-F9A2-49C2-9BE8-2F1F8EAD058D}.Publish|Any CPU.Build.0 = Debug|Any CPU
+ {AFB48153-F9A2-49C2-9BE8-2F1F8EAD058D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AFB48153-F9A2-49C2-9BE8-2F1F8EAD058D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/BoxVRPlaylistManagerNETCore/App.xaml b/BoxVRPlaylistManagerNETCore/App.xaml
new file mode 100644
index 0000000..65227e1
--- /dev/null
+++ b/BoxVRPlaylistManagerNETCore/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/BoxVRPlaylistManagerNETCore/App.xaml.cs b/BoxVRPlaylistManagerNETCore/App.xaml.cs
new file mode 100644
index 0000000..6cf3fe9
--- /dev/null
+++ b/BoxVRPlaylistManagerNETCore/App.xaml.cs
@@ -0,0 +1,215 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Configuration;
+using System.Data;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Windows;
+using BoxVRPlaylistManagerNETCore.Helpers;
+using BoxVRPlaylistManagerNETCore.UI;
+using log4net;
+using Microsoft.Win32;
+
+namespace BoxVRPlaylistManagerNETCore
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ const string DEFAULT_BOXVR_APPDATA = @"%userprofile%\AppData\LocalLow\FITXR\BOXVR";
+ const string REGISTRY_UNINSTALL_KEY = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
+ const string REGISTRY_OCULUS_LIB_KEY = @"Software\Oculus VR, LLC\Oculus\Libraries";
+ const string REGISTRY_STEAM_KEY = @"HKEY_CURRENT_USER\Software\Valve\Steam";
+
+ public const string BoxVRExePath = "BoxVRExePath";
+ public const string BoxVRAppDataPath = "BoxVRAppDataPath";
+
+ private static ILog _log = LogManager.GetLogger(typeof(App));
+ public static JsonConfiguration Configuration;
+
+ [STAThread]
+ public static void Main()
+ {
+ AppDomain.CurrentDomain.UnhandledException += (sender, e) => _log.Error(e.ExceptionObject as Exception);
+
+ var app = new App();
+
+ Configuration = new JsonConfiguration("appsettings.json");
+
+
+ // check if BoxVRExePath is already set, if not try to locate it via registry or Steam
+ if(string.IsNullOrWhiteSpace(Configuration.BoxVRExePath))
+ {
+ _log.Debug("Starting search for BoxVR install location");
+ var location = LocateBoxVRExe();
+ _log.Debug($"Automatic search for BoxVR install location complete: {location}");
+ if(!string.IsNullOrWhiteSpace(location))
+ {
+ _log.Debug("Saving BoxVRExePath setting");
+ Configuration.BoxVRExePath = location;
+ //BoxVRPlaylistManagerNETCore.Properties.Settings.Default.Save();
+ }
+ }
+ _log.Debug($"BoxVRExePath setting: {Configuration.BoxVRExePath}");
+
+ // check if BoxVRAppDataPath is already set, if not set to default
+ if(string.IsNullOrWhiteSpace(Configuration.BoxVRAppDataPath))
+ {
+ _log.Debug("BoxVRAppDataPath not set, setting default");
+ if(Directory.Exists(Environment.ExpandEnvironmentVariables(DEFAULT_BOXVR_APPDATA)))
+ {
+ Configuration.BoxVRAppDataPath = DEFAULT_BOXVR_APPDATA;
+ //BoxVRPlaylistManagerNETCore.Properties.Settings.Default.Save();
+ }
+ else
+ {
+ _log.Debug($"Directory does not exist: {DEFAULT_BOXVR_APPDATA}");
+ }
+ }
+ _log.Debug($"BoxVRAppDataPath setting: {Configuration.BoxVRAppDataPath}");
+
+ var mainWindow = new MainWindow();
+ app.Run(mainWindow);
+ }
+
+ private static string LocateBoxVRExe()
+ {
+ _log.Debug("Searching registry for BoxVR install location");
+ using(var key = Registry.LocalMachine.OpenSubKey(REGISTRY_UNINSTALL_KEY))
+ {
+ foreach(var subkey_name in key.GetSubKeyNames())
+ {
+ using(var subkey = key.OpenSubKey(subkey_name))
+ {
+ _log.Debug($"Checking reg key HKLM\\{REGISTRY_UNINSTALL_KEY}\\{subkey_name}");
+ if(string.Equals(subkey.GetValue("DisplayName")?.ToString(), "BOXVR", StringComparison.OrdinalIgnoreCase))
+ {
+ var location = subkey.GetValue("InstallLocation")?.ToString();
+ if(location != null)
+ {
+ _log.Debug($"BoxVR location found in reg key HKLM\\{REGISTRY_UNINSTALL_KEY}\\{subkey_name}: {location}");
+ return location;
+ }
+ }
+ }
+ }
+ }
+
+ _log.Debug("Searching registry for Oculus library location");
+ using(var key = Registry.CurrentUser.OpenSubKey(REGISTRY_OCULUS_LIB_KEY))
+ {
+ if(key == null)
+ {
+ _log.Debug($"Couldn't locate Oculus registry key: {REGISTRY_OCULUS_LIB_KEY}");
+ }
+ else
+ {
+ var mountPoints = new List();
+ foreach(var subkey_name in key.GetSubKeyNames())
+ {
+ using(var subkey = key.OpenSubKey(subkey_name))
+ {
+ _log.Debug($"Checking reg key HKCU\\{REGISTRY_OCULUS_LIB_KEY}\\{subkey_name}");
+ var libPath = subkey.GetValue("Path")?.ToString();
+ if(libPath == null)
+ {
+ _log.Debug($"No 'Path' value found, moving on");
+ }
+ else
+ {
+ _log.Debug($"Path found: {libPath}, searching for 'fitxr-boxvr'");
+ var volumeIdMatch = Regex.Match(libPath, @"(\\\\\?\\Volume{.*?}\\)(.*)");
+ if(volumeIdMatch.Success)
+ {
+ _log.Debug($"Getting mount points for volume {volumeIdMatch.Groups[1].Value}");
+ try
+ {
+ var _mountPoints = SafeNativeMethods.GetMountPointsForVolume(volumeIdMatch.Groups[1].Value);
+ _log.Debug($"Found the following mountpoints for {volumeIdMatch.Groups[1].Value}:\r\n{string.Join("\r\n", _mountPoints)}");
+ mountPoints.AddRange(_mountPoints.Select(m => Path.Combine(m, volumeIdMatch.Groups[2].Value)));
+ }
+ catch(Win32Exception ex)
+ {
+ _log.Error(ex);
+ }
+ }
+ else
+ {
+ _log.Debug("Failed to find volume GUID path");
+ }
+ }
+ }
+ }
+
+ foreach(var mountPoint in mountPoints)
+ {
+ var location = Path.Combine(mountPoint, "Software", "fitxr-boxvr", "BoxVR_Oculus");
+ if(File.Exists(Path.Combine(location, "BoxVR.exe")))
+ {
+ _log.Debug($"BoxVR located in Oculus library: {location}");
+ return location;
+ }
+ }
+ }
+ }
+
+ _log.Debug("Searching registry for Steam install location");
+ var steamPath = Registry.GetValue(REGISTRY_STEAM_KEY, "SteamPath", null)?.ToString();
+ if(steamPath != null)
+ {
+ _log.Debug($"{REGISTRY_STEAM_KEY}/SteamPath: {steamPath}");
+ var steamConfigPath = Path.Combine(steamPath, "config/config.vdf");
+ if(File.Exists(steamConfigPath))
+ {
+ var steamConfig = File.ReadAllText(steamConfigPath);
+ var matches = Regex.Matches(steamConfig, @"""BaseInstallFolder_\d\""\s+""(.*?)""");
+ if(matches.Count > 0)
+ {
+ foreach(Match match in matches)
+ {
+ var libraryPath = match.Groups[1].Value;
+ if(Directory.Exists(libraryPath))
+ {
+ _log.Debug($"Searching Steam library for BoxVR: {libraryPath}");
+ var BoxVRExePath = Path.Combine(libraryPath, "steamapps", "common", "BoxVR");
+ if(File.Exists(Path.Combine(BoxVRExePath, "BoxVR.exe")))
+ {
+ _log.Debug($"BoxVR.exe located at {BoxVRExePath}");
+ return BoxVRExePath;
+ }
+ else
+ {
+ _log.Debug($"Could not find BoxVR.exe in library: {libraryPath}");
+ }
+ }
+ else
+ {
+ _log.Debug($"Steam library does not exist: {libraryPath}");
+ }
+ }
+ }
+ else
+ {
+ _log.Debug($"Failed to find Steam library locations in {steamConfigPath}");
+ _log.Debug(steamConfig);
+ }
+ }
+ else
+ {
+ _log.Debug($"No Steam config found at {steamConfigPath}");
+ }
+ }
+ else
+ {
+ _log.Debug("Failed to load HKCU/Software/Valve/Steam/SteamPath");
+ }
+
+ _log.Debug("Could not automatically find BoxVR install location");
+ return string.Empty;
+ }
+ }
+}
diff --git a/BoxVRPlaylistManagerNETCore/AssemblyInfo.cs b/BoxVRPlaylistManagerNETCore/AssemblyInfo.cs
new file mode 100644
index 0000000..8b5504e
--- /dev/null
+++ b/BoxVRPlaylistManagerNETCore/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/BoxVRPlaylistManagerNETCore/BoxVRPlaylistManagerNETCore.csproj b/BoxVRPlaylistManagerNETCore/BoxVRPlaylistManagerNETCore.csproj
new file mode 100644
index 0000000..0d6a0d1
--- /dev/null
+++ b/BoxVRPlaylistManagerNETCore/BoxVRPlaylistManagerNETCore.csproj
@@ -0,0 +1,52 @@
+
+
+
+ WinExe
+ net5.0-windows
+ true
+ true
+
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ MainWindow.xaml
+
+
+
+
+
+ SettingsWindow.xaml
+
+
+
+
+
+ Always
+
+
+
+
+
+ Designer
+
+
+
+
diff --git a/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/Bar.cs b/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/Bar.cs
new file mode 100644
index 0000000..999558c
--- /dev/null
+++ b/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/Bar.cs
@@ -0,0 +1,26 @@
+using Newtonsoft.Json;
+
+namespace BoxVRPlaylistManagerNETCore.FitXr.BeatStructure
+{
+ public class Bar
+ {
+ [JsonProperty("_avgEnergy")]
+ public float _avgEnergy;
+ [JsonProperty("_duration")]
+ public float _duration;
+ [JsonProperty("_startTime")]
+ public float _startTime;
+ [JsonProperty("_beatIndex")]
+ public int _beatIndex;
+
+ public float Bpm => Bar.DurationToBpm(this._duration);
+
+ public float EndTime => this._startTime + this._duration;
+
+ public static float DurationToBpm(float duration) => 240f / duration;
+
+ public static float BpmToDuration(float bpm) => 240f / bpm;
+
+ public override string ToString() => "Start:" + (object)this._startTime + " Length:" + (object)this._duration;
+ }
+}
diff --git a/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/BarList.cs b/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/BarList.cs
new file mode 100644
index 0000000..4d71eeb
--- /dev/null
+++ b/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/BarList.cs
@@ -0,0 +1,161 @@
+using System;
+using System.Collections.Generic;
+
+namespace BoxVRPlaylistManagerNETCore.FitXr.BeatStructure
+{
+ public class BarList
+ {
+ public List _bars;
+
+ protected float CalcMedianLength()
+ {
+ List barList = new List((IEnumerable)this._bars);
+ barList.Sort((Comparison)((x, y) => (int)Math.Sign(x._duration - y._duration)));
+ return barList[barList.Count / 2]._duration;
+ }
+
+ public void HalfSpeed()
+ {
+ for(int index = 0; index < this._bars.Count - 1; ++index)
+ {
+ Bar bar1 = this._bars[index];
+ Bar bar2 = this._bars[index + 1];
+ bar1._duration += bar2._duration;
+ bar1._avgEnergy = (float)(((double)bar1._avgEnergy + (double)bar2._avgEnergy) / 2.0);
+ this._bars.RemoveAt(index + 1);
+ }
+ }
+
+ public void DoubleSpeed(BeatList beats)
+ {
+ for(int index = 0; index < this._bars.Count; index += 2)
+ {
+ Bar bar1 = this._bars[index];
+ Bar bar2 = new Bar();
+ this._bars.Insert(index + 1, bar2);
+ float num = bar1._duration / 2f;
+ bar1._duration = num;
+ bar2._duration = num;
+ bar2._startTime = bar1._startTime + num;
+ }
+ }
+
+ public void UpdateEnergies(List beats)
+ {
+ for(int index1 = 0; index1 < this._bars.Count; ++index1)
+ {
+ Bar bar = this._bars[index1];
+ for(int index2 = 0; index2 <= 3; ++index2)
+ {
+ int index3 = bar._beatIndex + index2;
+ if(index3 < beats.Count && beats[index3]._beatInBar == index2 + 1)
+ bar._avgEnergy += beats[index3]._magnitude;
+ }
+ bar._avgEnergy /= 4f;
+ }
+ }
+
+ public void FillEmptyRegions(float deltaBpm, float songLength)
+ {
+ float duration1 = this.CalcMedianLength();
+ while(true)
+ {
+ float num = this._bars[0]._startTime - duration1;
+ if((double)num >= 0.0)
+ this._bars.Insert(0, new Bar()
+ {
+ _duration = duration1,
+ _startTime = num
+ });
+ else
+ break;
+ }
+ float duration2 = Bar.BpmToDuration(Bar.DurationToBpm(duration1) + deltaBpm);
+ for(int index = 0; index < this._bars.Count - 1; ++index)
+ {
+ double startTime = (double)this._bars[index + 1]._startTime;
+ float endTime = this._bars[index].EndTime;
+ double num1 = (double)endTime;
+ float num2 = (float)(startTime - num1);
+ if((double)num2 >= (double)duration1)
+ this._bars.Insert(index + 1, new Bar()
+ {
+ _duration = duration1,
+ _startTime = endTime
+ });
+ else if((double)num2 >= (double)duration2)
+ this._bars.Insert(index + 1, new Bar()
+ {
+ _duration = num2,
+ _startTime = endTime
+ });
+ else if((double)num2 > 0.0)
+ this._bars[index]._duration += num2;
+ }
+ while(true)
+ {
+ Bar bar = this._bars[this._bars.Count - 1];
+ if((double)bar.EndTime + (double)duration1 <= (double)songLength)
+ this._bars.Add(new Bar()
+ {
+ _startTime = bar.EndTime,
+ _duration = duration1
+ });
+ else
+ break;
+ }
+ }
+
+ public void RemoveDeviants(float bpmDelta)
+ {
+ float num = 240f / this.CalcMedianLength();
+ int index = 0;
+ while(index < this._bars.Count)
+ {
+ Bar bar = this._bars[index];
+ if((double)Math.Abs(bar.Bpm - num) > (double)bpmDelta)
+ this._bars.Remove(bar);
+ else
+ ++index;
+ }
+ }
+
+ public void CreateFromBeats(List beats)
+ {
+ this._bars = new List();
+ Bar bar = (Bar)null;
+ for(int index = 0; index < beats.Count; ++index)
+ {
+ BeatInfo beat = beats[index];
+ if(beat._beatInBar == 1)
+ {
+ bar = new Bar()
+ {
+ _startTime = beat._triggerTime,
+ _beatIndex = index
+ };
+ this._bars.Add(bar);
+ }
+ if(bar != null && beat._beatInBar == 4)
+ bar._duration = beat._triggerTime + beat._beatLength - bar._startTime;
+ }
+ this.UpdateEnergies(beats);
+ }
+
+ public int FindClosestBar(float time, List beats)
+ {
+ float num1 = float.MaxValue;
+ int num2 = -1;
+ for(int index = 0; index < this._bars.Count; ++index)
+ {
+ float num3 = Math.Abs(this._bars[index]._startTime - time);
+ if((double)num3 < (double)num1)
+ {
+ num2 = index;
+ num1 = num3;
+ }
+ }
+ return num2;
+ }
+ }
+}
diff --git a/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/Beat.cs b/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/Beat.cs
new file mode 100644
index 0000000..8486f66
--- /dev/null
+++ b/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/Beat.cs
@@ -0,0 +1,16 @@
+namespace BoxVRPlaylistManagerNETCore.FitXr.BeatStructure
+{
+ public class Beat
+ {
+ public float length;
+ public float bpm;
+ public int index;
+
+ public Beat(float length, float bpm, int index)
+ {
+ this.length = length;
+ this.bpm = bpm;
+ this.index = index;
+ }
+ }
+}
diff --git a/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/BeatInfo.cs b/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/BeatInfo.cs
new file mode 100644
index 0000000..37c33c5
--- /dev/null
+++ b/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/BeatInfo.cs
@@ -0,0 +1,45 @@
+namespace BoxVRPlaylistManagerNETCore.FitXr.BeatStructure
+{
+ public class BeatInfo
+ {
+ public int _index;
+ public int _beatInBar;
+ public float _magnitude;
+ public float _triggerTime;
+ public float _beatLength;
+ public float _bpm;
+ public bool _isLastBeat;
+ public Segment _segment;
+
+ public BeatInfo(
+ int index,
+ float triggerTime,
+ float mag,
+ float beatLength,
+ float bpm,
+ int beatInBar,
+ bool isLastBeat)
+ {
+ this._beatInBar = beatInBar;
+ this._index = index;
+ this._triggerTime = triggerTime;
+ this._magnitude = mag;
+ this._beatLength = beatLength;
+ this._bpm = bpm;
+ this._isLastBeat = isLastBeat;
+ }
+
+ public BeatInfo(int index, float triggerTime, float mag, Beat beat, bool isLastBeat)
+ : this(index, triggerTime, mag, beat.length, beat.bpm, -1, isLastBeat)
+ {
+ }
+
+ public override string ToString()
+ {
+ string str = string.Format("{2,3} [{0,6:F2} s][{1,6} bpm][{3, 6:F2} magnitude]", (object)this._triggerTime, (object)this._bpm, (object)this._index, (object)this._magnitude);
+ if(this._segment != null)
+ str += this._segment.ToString();
+ return str + "\n";
+ }
+ }
+}
diff --git a/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/BeatList.cs b/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/BeatList.cs
new file mode 100644
index 0000000..ea96c3c
--- /dev/null
+++ b/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/BeatList.cs
@@ -0,0 +1,101 @@
+using System;
+using System.Collections.Generic;
+using BoxVRPlaylistManagerNETCore.FitXr.Tools;
+using log4net;
+
+namespace BoxVRPlaylistManagerNETCore.FitXr.BeatStructure
+{
+ public class BeatList
+ {
+ public List _beats;
+ public float AverageBpm;
+ public ILog _log = LogManager.GetLogger(typeof(BeatList));
+
+ private float CalcMedianBpm(List beatList)
+ {
+ List beatInfoList = new List((IEnumerable)beatList);
+ beatInfoList.Sort((Comparison)((x, y) => (int)Math.Sign(x._bpm - y._bpm)));
+ return beatInfoList[beatInfoList.Count / 2]._bpm;
+ }
+
+ public void CreateFromBars(BarList barList)
+ {
+ this._beats = new List();
+ int index1 = 0;
+ for(int index2 = 0; index2 < barList._bars.Count; ++index2)
+ {
+ Bar bar = barList._bars[index2];
+ bar._beatIndex = index1;
+ float startTime = bar._startTime;
+ float num = bar._duration / 4f;
+ for(int beatInBar = 1; beatInBar < 5; ++beatInBar)
+ {
+ this._beats.Add(new BeatInfo(index1, startTime, -1f, -1f, -1f, beatInBar, false));
+ ++index1;
+ startTime += num;
+ }
+ }
+ for(int index2 = 0; index2 < this._beats.Count; ++index2)
+ {
+ this._beats[index2]._beatLength = index2 >= this._beats.Count - 1 ? this._beats[index2 - 1]._beatLength : this._beats[index2 + 1]._triggerTime - this._beats[index2]._triggerTime;
+ this._beats[index2]._bpm = 60f / this._beats[index2]._beatLength;
+ }
+ this.AverageBpm = this.CalcMedianBpm(this._beats);
+ for(int index2 = 0; index2 < this._beats.Count; ++index2)
+ this._beats[index2]._bpm = this.AverageBpm;
+ }
+
+ public void UpdateEnergiesFromLines(string[] lines)
+ {
+ int index1 = 0;
+ int index2 = 0;
+ this._beats[0]._magnitude = 0.0f;
+ do
+ {
+ float val1 = -1f;
+ float val2 = -1f;
+ Format.FloatsFromCsvLine(lines[index1], out val1, out val2);
+ while((double)val1 >= (double)this._beats[index2 + 1]._triggerTime)
+ {
+ ++index2;
+ this._beats[index2]._magnitude = 0.0f;
+ if(index2 == this._beats.Count - 1)
+ break;
+ }
+ this._beats[index2]._magnitude += val2;
+ ++index1;
+ }
+ while(index1 != lines.Length && index2 != this._beats.Count - 1);
+ }
+
+ public void CreateFromLines(string[] lines)
+ {
+ this._beats = new List();
+ int index1 = 0;
+ for(int index2 = 0; index2 < lines.Length; ++index2)
+ {
+ List stringList = new List((IEnumerable)lines[index2].Split('\t'));
+ float triggerTime = float.Parse(stringList[0]);
+ int beatInBar = int.Parse(stringList[1]);
+ this._beats.Add(new BeatInfo(index1, triggerTime, -1f, -1f, -1f, beatInBar, false));
+ ++index1;
+ }
+ for(int index2 = 0; index2 < this._beats.Count - 1; ++index2)
+ {
+ this._beats[index2]._beatLength = this._beats[index2 + 1]._triggerTime - this._beats[index2]._triggerTime;
+ this._beats[index2]._bpm = 60f / this._beats[index2]._beatLength;
+ }
+ if(this._beats.Count > 0)
+ {
+ this.AverageBpm = this.CalcMedianBpm(this._beats);
+ }
+ else
+ {
+ _log.Debug("MadMom failure");
+ this.AverageBpm = 120f;
+ }
+ for(int index2 = 0; index2 < this._beats.Count; ++index2)
+ this._beats[index2]._bpm = this.AverageBpm;
+ }
+ }
+}
diff --git a/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/BeatStructureBase.cs b/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/BeatStructureBase.cs
new file mode 100644
index 0000000..4e46b1b
--- /dev/null
+++ b/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/BeatStructureBase.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using Newtonsoft.Json;
+
+namespace BoxVRPlaylistManagerNETCore.FitXr.BeatStructure
+{
+ public class BeatStructureBase
+ {
+
+ public event EventHandler _buildCompleteEvent;
+
+ protected bool _isBuilt;
+
+ public virtual List Beats => (List)null;
+
+ [JsonProperty("AverageBpm")]
+ public float AverageBpm { get; protected set; }
+
+ [JsonProperty("_isBuilt")]
+ public bool IsBuilt => this._isBuilt;
+
+ public virtual void Build()
+ {
+ }
+
+ public virtual BeatInfo GetBeatInInterval(float fromTime, float toTime)
+ {
+ foreach(BeatInfo beat in this.Beats)
+ {
+ if((double)beat._triggerTime >= (double)fromTime && (double)beat._triggerTime < (double)toTime)
+ return beat;
+ }
+ return (BeatInfo)null;
+ }
+
+ protected float CalcAverageBpm(List beatList)
+ {
+ float num = 0.0f;
+ foreach(BeatInfo beat in this.Beats)
+ num += beat._bpm;
+ return num / (float)this.Beats.Count;
+ }
+
+ protected void RaiseBuildCompleteEvent()
+ {
+ _buildCompleteEvent?.Invoke(this, EventArgs.Empty);
+ }
+
+ public override string ToString()
+ {
+ string str = "";
+ foreach(BeatInfo beat in this.Beats)
+ str += beat.ToString();
+ return str;
+ }
+ }
+}
diff --git a/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/BeatStructureMadmom.cs b/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/BeatStructureMadmom.cs
new file mode 100644
index 0000000..265b3ce
--- /dev/null
+++ b/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/BeatStructureMadmom.cs
@@ -0,0 +1,97 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using log4net;
+using Newtonsoft.Json;
+
+namespace BoxVRPlaylistManagerNETCore.FitXr.BeatStructure
+{
+ public class BeatStructureMadmom : BeatStructureBase
+ {
+ private ILog _log = LogManager.GetLogger(typeof(BeatStructureMadmom));
+
+
+ [JsonProperty("_barList")]
+ public BarList _barList;
+ [JsonProperty("_segmentList")]
+ public SegmentList _segmentList;
+ [JsonProperty("")]
+ public int _dataVersion = BeatStructureMadmom.DATA_VERSION;
+ public static int DATA_VERSION = 3;
+ [JsonProperty("_beatList")]
+ public BeatList _beatList;
+ private const float MAX_SPEED_BPM = 190f;
+ private const float MIN_SPEED_BPM = 90f;
+ private UserSongClip _userSong;
+ private MadmomProcess _madmomProcess;
+ private VampProcess _vampProcessSegments;
+ private VampProcess _vampProcessEnergies;
+
+ public override List Beats => this._beatList._beats;
+
+ public BeatStructureMadmom()
+ {
+ }
+
+ public BeatStructureMadmom(
+ UserSongClip userSong,
+ MadmomProcess madmomProcess,
+ VampProcess vampProcessSegments,
+ VampProcess vampProcessEnergies)
+ {
+ this._madmomProcess = madmomProcess;
+ this._userSong = userSong;
+ this._vampProcessSegments = vampProcessSegments;
+ this._vampProcessEnergies = vampProcessEnergies;
+ this._isBuilt = false;
+ }
+
+ public override void Build() => this._madmomProcess.Apply(this._userSong.wavPath, OnBeatsTracked);
+
+ private void OnBeatsTracked()
+ {
+ this._beatList = new BeatList();
+ this._beatList.CreateFromLines(this._madmomProcess._resultEntries);
+ this._barList = new BarList();
+ this._barList.CreateFromBeats(this._beatList._beats);
+ this._barList.RemoveDeviants(2f);
+ this._barList.FillEmptyRegions(8f, this._userSong.trackData.duration);
+ if((double)this._barList._bars[0].Bpm > 190.0)
+ this._barList.HalfSpeed();
+ else if((double)this._barList._bars[0].Bpm < 90.0)
+ this._barList.DoubleSpeed(this._beatList);
+ this._beatList.CreateFromBars(this._barList);
+ this.AverageBpm = this._beatList.AverageBpm;
+ // ISSUE: method pointer
+ this._vampProcessEnergies.Apply("vamp:bbc-vamp-plugins:bbc-energy", this._userSong.wavPath, OnEnergiesTracked);
+ }
+
+ private void OnEnergiesTracked()
+ {
+ this._beatList.UpdateEnergiesFromLines(this._vampProcessEnergies._resultEntries);
+ this._barList.UpdateEnergies(this.Beats);
+ // ISSUE: method pointer
+ this._vampProcessSegments.Apply("vamp:qm-vamp-plugins:qm-segmenter", this._userSong.wavPath,OnSegmentsTracked);
+ }
+
+ private void OnSegmentsTracked()
+ {
+ this._segmentList = new SegmentList(this._vampProcessSegments._resultEntries, this.Beats, this._barList);
+ _log.Debug(this._segmentList.ToString());
+ List segments = this._segmentList._segments;
+ for(int index1 = 0; index1 < segments.Count; ++index1)
+ {
+ for(int index2 = 0; index2 < segments[index1]._numBeats; ++index2)
+ this.Beats[segments[index1]._startBeatIndex + index2]._segment = segments[index1];
+ }
+ this.OnBuildComplete(); }
+
+ private void OnBuildComplete()
+ {
+ this._isBuilt = true;
+ RaiseBuildCompleteEvent();
+ }
+ }
+ }
diff --git a/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/FFmpegJob.cs b/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/FFmpegJob.cs
new file mode 100644
index 0000000..7a87de9
--- /dev/null
+++ b/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/FFmpegJob.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace BoxVRPlaylistManagerNETCore.FitXr.BeatStructure
+{
+ public class FFmpegJob
+ {
+ public Action _onFinished;
+ public string _inputPath;
+ public string _outputPath;
+ public string _message;
+
+ public virtual string GetCommand() => "";
+ }
+}
diff --git a/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/FFmpegJobConvert.cs b/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/FFmpegJobConvert.cs
new file mode 100644
index 0000000..fd27472
--- /dev/null
+++ b/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/FFmpegJobConvert.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace BoxVRPlaylistManagerNETCore.FitXr.BeatStructure
+{
+ public class FFmpegJobConvert : FFmpegJob
+ {
+ public FFmpegJobConvert(string inputPath, string outputPath, Action onFinished)
+ {
+ this._inputPath = inputPath;
+ this._outputPath = outputPath;
+ this._onFinished = onFinished;
+ }
+
+ public override string GetCommand() => string.Join(" ", new string[7]
+ {
+ "-y",
+ "-i",
+ "\"" + this._inputPath + "\"",
+ "-ar 44100 ",
+ "-acodec pcm_s16le ",
+ "-nostdin -loglevel error",
+ "\"" + this._outputPath + "\""
+ });
+ }
+}
diff --git a/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/FFmpegQueue.cs b/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/FFmpegQueue.cs
new file mode 100644
index 0000000..1f96e1d
--- /dev/null
+++ b/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/FFmpegQueue.cs
@@ -0,0 +1,71 @@
+using System.Collections;
+using System.Diagnostics;
+using System.IO;
+using System.Threading;
+using BoxVRPlaylistManagerNETCore.FitXr.Enums;
+using BoxVRPlaylistManagerNETCore.Helpers;
+using log4net;
+
+namespace BoxVRPlaylistManagerNETCore.FitXr.BeatStructure
+{
+ public class FFmpegQueue
+ {
+ private ILog _log = LogManager.GetLogger(typeof(FFmpegQueue));
+ public static FFmpegQueue instance = instance == null ? new FFmpegQueue() : instance;
+
+ public void Queue(FFmpegJob job) => RunFFMPEG(job);
+
+ public static string binaryPath => Path.Combine(Paths.StreamingAssetsPath, @"ffmpeg\bin\ffmpeg.exe");
+
+ private void RunFFMPEG(FFmpegJob job)
+ {
+ Directory.CreateDirectory(Paths.TrackDataFolder(LocationMode.PlayerData));
+ _log.Debug(("FFMPEG start: " + FFmpegQueue.binaryPath + " " + job.GetCommand()));
+ bool done = false;
+ string exepath = FFmpegQueue.binaryPath;
+ string command = job.GetCommand();
+ new Thread((ThreadStart)(() =>
+ {
+ string output = "";
+ Process process = new Process();
+ process.StartInfo.RedirectStandardOutput = true;
+ process.StartInfo.RedirectStandardError = true;
+ process.StartInfo.UseShellExecute = false;
+ process.StartInfo.CreateNoWindow = true;
+ process.StartInfo.FileName = exepath;
+ process.StartInfo.Arguments = command;
+ process.OutputDataReceived += (DataReceivedEventHandler)((s, e) => output += e.Data);
+ process.ErrorDataReceived += (DataReceivedEventHandler)((s, e) => output += e.Data);
+ process.Start();
+ process.WaitForExit();
+ process.Close();
+ job._message = output;
+ done = true;
+ })).Start();
+ while(!done) { }
+ job._onFinished.Invoke(job);
+ _log.Debug("FFMEG done");
+ }
+
+ public bool ExtractDurationFromFFmpegOutput(string output, out int minutes, out int seconds)
+ {
+ minutes = -1;
+ seconds = -1;
+ string str1 = "Duration: ";
+ string str2 = "xx:xx:xx.xx";
+ int startIndex = output.IndexOf(str1) + str1.Length;
+ string[] strArray = output.Substring(startIndex, str2.Length).Split(':', '.');
+ if(strArray == null || strArray.Length != 4)
+ return false;
+ int[] numArray = new int[strArray.Length];
+ for(int index = 0; index < strArray.Length; ++index)
+ {
+ if(!int.TryParse(strArray[index], out numArray[index]))
+ return false;
+ }
+ minutes = numArray[0] * 60 + numArray[1];
+ seconds = numArray[2];
+ return true;
+ }
+ }
+}
diff --git a/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/MadmomProcess.cs b/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/MadmomProcess.cs
new file mode 100644
index 0000000..e9bb33b
--- /dev/null
+++ b/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/MadmomProcess.cs
@@ -0,0 +1,104 @@
+
+using System;
+using System.Collections;
+using System.Diagnostics;
+using System.IO;
+using System.Threading;
+using BoxVRPlaylistManagerNETCore.Helpers;
+using log4net;
+
+namespace BoxVRPlaylistManagerNETCore.FitXr.BeatStructure
+{
+ public class MadmomProcess
+ {
+ private ILog _log = LogManager.GetLogger(typeof(MadmomProcess));
+
+
+ public static string madmonOutputPath;
+ public string[] _resultEntries;
+ public string _output;
+ private const bool SHOW_WINDOW = false;
+ private string processPath;
+ private string outputFileName;
+
+ private string BinaryPath => Path.Combine(Paths.StreamingAssetsPath, @"DBNDownBeatTracker\DBNDownBeatTracker.exe");
+
+ public MadmomProcess()
+ {
+ madmonOutputPath = Path.Combine(Paths.PersistentDataPath, "Temp");
+ this.processPath = this.BinaryPath;
+ if(Directory.Exists(MadmomProcess.madmonOutputPath))
+ {
+ return;
+ }
+ Directory.CreateDirectory(MadmomProcess.madmonOutputPath);
+ }
+
+ private string CreateCommandArgs(string musicPath, string outputPath) => string.Format("--beats_per_bar 4 single \"{0}\" -o \"{1}\" -j 1", (object)musicPath, (object)outputPath);
+
+ public void Apply(string musicPath, Action successAction)
+ {
+ ProcessMadmom(musicPath, successAction);
+ }
+
+ private void ProcessMadmom(string musicPath, Action successAction)
+ {
+ outputFileName = Path.GetFileNameWithoutExtension(musicPath) + ".madmom.txt";
+ string resultOutPath = Path.Combine(MadmomProcess.madmonOutputPath, Path.GetFileNameWithoutExtension(musicPath) + ".madmom.txt");
+ string ffmpegPath = Path.GetDirectoryName(FFmpegQueue.binaryPath);
+ string arguments = this.CreateCommandArgs(musicPath, resultOutPath);
+ string path = MadmomProcess.madmonOutputPath + this.outputFileName;
+ File.Delete(resultOutPath);
+ File.Delete(path);
+ bool isProcessComplete = false;
+ _log.Debug(arguments);
+ Process process = (Process)null;
+ new Thread((ThreadStart)(() =>
+ {
+ _log.Debug("Madmom started");
+ process = new Process()
+ {
+ StartInfo = {
+ FileName = this.processPath,
+ UseShellExecute = false,
+ EnvironmentVariables = {
+ {
+ "PROGRESS_PATH",
+ MadmomProcess.madmonOutputPath + this.outputFileName
+ }
+ }
+ }
+ };
+ process.StartInfo.EnvironmentVariables["PATH"] = process.StartInfo.EnvironmentVariables["PATH"] + ";" + ffmpegPath;
+ process.StartInfo.CreateNoWindow = true;
+ process.StartInfo.Arguments = arguments;
+ isProcessComplete = false;
+ process.Start();
+ process.PriorityClass = ProcessPriorityClass.BelowNormal;
+ process.WaitForExit();
+ isProcessComplete = true;
+ })).Start();
+ _log.Debug("Waiting for madmom");
+ while(!isProcessComplete){ }
+ while(!process.HasExited){ }
+ int exitCode = process.ExitCode;
+ process.Close();
+ _log.Debug("Madmom finished : error code= " + (object)exitCode);
+ this._resultEntries = new string[0];
+ if(File.Exists(resultOutPath))
+ {
+ this._resultEntries = File.ReadAllLines(resultOutPath);
+ }
+
+ if(exitCode == -1 || this._resultEntries.Length == 0)
+ {
+ throw new System.Exception("Process error");
+ }
+ else
+ {
+ successAction.Invoke();
+ }
+ File.Delete(resultOutPath);
+ }
+ }
+}
diff --git a/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/PlaylistManager.cs b/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/PlaylistManager.cs
new file mode 100644
index 0000000..b26854c
--- /dev/null
+++ b/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/PlaylistManager.cs
@@ -0,0 +1,332 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Collections;
+using System.IO;
+using BoxVRPlaylistManagerNETCore.FitXr.Models;
+using BoxVRPlaylistManagerNETCore.Helpers;
+using BoxVRPlaylistManagerNETCore.FitXr.Enums;
+using Newtonsoft.Json;
+using BoxVRPlaylistManagerNETCore.FitXr.Utility;
+using log4net;
+
+namespace BoxVRPlaylistManagerNETCore.FitXr.BeatStructure
+{
+ public class PlaylistManager
+ {
+ public static int MAX_NUM_ENTRIES = 20;
+ public static PlaylistManager instance = instance == null ? new PlaylistManager() : instance;
+ private string playlistsPath;
+ public List workoutPlaylists;
+ private List playlistQueue;
+ private bool isProcessing;
+ private ILog _log = LogManager.GetLogger(typeof(PlaylistManager));
+
+ public event PlaylistManager.OnTrackAdditionComplete onTrackAdditionComplete;
+
+ public PlaylistManager()
+ {
+ Directory.CreateDirectory(Paths.RootDataFolder(LocationMode.PlayerData));
+ Directory.CreateDirectory(Paths.WorkoutDefinitionFolder(LocationMode.PlayerData, Game.BoxVR));
+ Directory.CreateDirectory(Paths.TrackDataFolder(LocationMode.PlayerData));
+ Directory.CreateDirectory(Paths.DefinitionsFolder(LocationMode.PlayerData));
+ Directory.CreateDirectory(Paths.ImgDataFolder(LocationMode.PlayerData));
+ this.playlistsPath = Paths.WorkoutDefinitionFolder(LocationMode.PlayerData, Game.BoxVR);
+ if(Directory.Exists(this.playlistsPath))
+ return;
+ Directory.CreateDirectory(this.playlistsPath);
+ }
+
+
+ public List GetAllWorkoutPlaylists() => this.workoutPlaylists;
+
+ public List ReturnAllPlaylistsFromDisk() => !Directory.Exists(this.playlistsPath) ? new List() : new List((IEnumerable)Tools.IO.GetVisibleFiles(this.playlistsPath, "*.play"));
+
+ public void LoadWorkoutPlaylists()
+ {
+ this.workoutPlaylists = this.LoadAllWorkoutPlaylistsFromDisk();
+ LoadPlaylistTrackDefinition();
+ foreach(var playlist in workoutPlaylists)
+ {
+ playlist.CalcTotalLength();
+ }
+ }
+
+ private void LoadPlaylistTrackDefinition()
+ {
+ while(TrackDataManager.instance == null) { }
+ while(!TrackDataManager.instance.trackListLoaded) { }
+ for(int index1 = 0; index1 < this.workoutPlaylists.Count; ++index1)
+ {
+ for(int index2 = 0; index2 < this.workoutPlaylists[index1].songs.Count; ++index2)
+ {
+ this.workoutPlaylists[index1].songs[index2].trackDefinition = new TrackDefinition();
+ this.workoutPlaylists[index1].songs[index2].trackDefinition.LoadTrackDefinition(this.workoutPlaylists[index1].songs[index2].trackDataName, LocationMode.PlayerData);
+ }
+ }
+ }
+
+ public List LoadAllWorkoutPlaylistsFromDisk()
+ {
+ List workoutPlaylistList = new List();
+ if(Directory.Exists(this.playlistsPath))
+ {
+ foreach(string visibleFile in Tools.IO.GetVisibleFiles(this.playlistsPath, "*.workoutplaylist.txt"))
+ {
+ WorkoutPlaylist workoutPlaylist = new WorkoutPlaylist();
+ string jsonString = File.ReadAllText(visibleFile);
+ workoutPlaylist.LoadFromJSON(jsonString);
+ workoutPlaylistList.Add(workoutPlaylist);
+ }
+ }
+ return workoutPlaylistList;
+ }
+
+ public void Saveplaylists()
+ {
+
+ for(int index = 0; index < this.workoutPlaylists.Count; ++index)
+ this.ExportPlaylistJson(this.workoutPlaylists[index]);
+ }
+
+ public void ExportPlaylistJson(WorkoutPlaylist playlist)
+ {
+ playlist.CalcTotalLength();
+ if(playlist.definition.originalName != playlist.definition.workoutName)
+ {
+ DeleteJsonFile(playlist.definition.originalName);
+ }
+ File.WriteAllText(Path.Combine(this.playlistsPath, playlist.definition.workoutName + ".workoutplaylist.txt"), JsonConvert.SerializeObject(playlist));
+ }
+
+ public string PlaylistJsonFromName(string playlistName)
+ {
+ string str1 = "";
+ string str2 = Path.Combine(this.playlistsPath, playlistName + ".play");
+ if((!File.Exists(str2) ? 0 : (new FileInfo(str2).Length > 0L ? 1 : 0)) != 0)
+ str1 = File.ReadAllText(str2);
+ return str1;
+ }
+
+ public void DeletePlaylist(string playlistName)
+ {
+ WorkoutPlaylist playlistByName = this.GetPlaylistByName(playlistName);
+ if(playlistByName == null)
+ return;
+ this.DeletePlaylist(playlistByName);
+ }
+
+ public void DeletePlaylist(WorkoutPlaylist playlist)
+ {
+ this.DeleteJsonFile(playlist.definition.workoutName);
+ this.workoutPlaylists.Remove(playlist);
+ //this.Saveplaylists();
+ }
+
+ public List ReturnPlaylistTrackDefinitions(
+ WorkoutPlaylist playlist)
+ {
+ List trackDefinitionList = new List();
+ if(playlist == null || playlist.songs == null)
+ return (List)null;
+ for(int index = 0; index < playlist.songs.Count; ++index)
+ {
+ playlist.songs[index].trackDefinition = TrackDataManager.instance.GetTrackDefinition(playlist.songs[index].trackDataName);
+ trackDefinitionList.Add(playlist.songs[index].trackDefinition);
+ }
+ return trackDefinitionList;
+ }
+
+ public void PlaylistEditName(WorkoutPlaylist playlist, string newName)
+ {
+ string workoutName = playlist.definition.workoutName;
+ if(newName == workoutName)
+ return;
+ playlist.definition.workoutName = newName;
+ this.ExportPlaylistJson(playlist);
+ this.DeleteJsonFile(workoutName);
+ }
+
+ private void DeleteJsonFile(string workoutName) => File.Delete(Path.Combine(this.playlistsPath, workoutName + ".workoutplaylist.txt"));
+
+ public SongDefinition PlaylistAddEntry(WorkoutPlaylist playlist, string fileName, LocationMode location)
+ {
+ if(this.playlistQueue == null)
+ this.playlistQueue = new List();
+ PlaylistManager.PlaylistToAdd playlistToAdd = new PlaylistManager.PlaylistToAdd()
+ {
+ playlistToAdd = playlist,
+ songToAdd = new SongDefinition()
+ };
+ playlistToAdd.songToAdd.trackDefinition = new TrackDefinition();
+ playlistToAdd.originalFilePath = fileName;
+ playlistToAdd.location = location;
+ this.playlistQueue.Add(playlistToAdd);
+ if(this.isProcessing)
+ throw new Exception("Can't batch add yet");
+ this.PlaylistAddProcess();
+ return playlistToAdd.songToAdd;
+ }
+
+ public void PlaylistAddProcess()
+ {
+ this.isProcessing = true;
+ string originalFilePath = this.playlistQueue[0].originalFilePath;
+ LocationMode location = this.playlistQueue[0].location;
+ _log.Debug("Location mode = " + location.ToString());
+ var trackHash = MD5.MD5Sum(Path.GetFileNameWithoutExtension(originalFilePath));
+ if(this.playlistQueue[0].songToAdd.trackDefinition.LoadTrackDefinition(trackHash, location))
+ {
+ this.playlistQueue[0].songToAdd.trackDataName = this.playlistQueue[0].songToAdd.trackDefinition.trackId.trackId;
+ this.playlistQueue[0].playlistToAdd.AddSong(this.playlistQueue[0].songToAdd);
+ this.ExportPlaylistJson(this.playlistQueue[0].playlistToAdd);
+ this.WaitingOver();
+ }
+ else
+ {
+ this.playlistQueue[0].songToAdd.trackDefinition.trackData = new TrackData();
+ this.playlistQueue[0].songToAdd.trackDefinition.trackData.PopulateTrackDataFromAudioFile(originalFilePath, location, Game.BoxVR);
+ WaitForTrackData();
+ }
+ }
+
+ private void WaitForTrackData()
+ {
+ while(this.playlistQueue[0].songToAdd.trackDefinition.trackData.trackDataState == TrackDataState.Loading) { }
+ if(this.playlistQueue[0].songToAdd.trackDefinition.trackData.trackDataState == TrackDataState.Ready)
+ {
+ this.playlistQueue[0].songToAdd.trackDataName = this.playlistQueue[0].songToAdd.trackDefinition.trackData.trackId.trackId;
+ this.playlistQueue[0].songToAdd.trackDefinition.LoadTrackDefinition(playlistQueue[0].songToAdd.trackDataName, playlistQueue[0].location);
+ this.playlistQueue[0].playlistToAdd.AddSong(this.playlistQueue[0].songToAdd);
+ this.ExportPlaylistJson(this.playlistQueue[0].playlistToAdd);
+ }
+ else
+ _log.Debug("TrackData failed");
+ this.WaitingOver();
+ }
+
+ private void WaitingOver()
+ {
+ this.playlistQueue.RemoveAt(0);
+ this.isProcessing = false;
+ if(this.playlistQueue.Count > 0)
+ {
+ this.PlaylistAddProcess();
+ }
+ else
+ {
+ if(this.onTrackAdditionComplete == null)
+ return;
+ this.onTrackAdditionComplete();
+ this.onTrackAdditionComplete = (PlaylistManager.OnTrackAdditionComplete)null;
+ }
+ }
+
+ public int ParsingQueueSize() => this.playlistQueue == null ? 0 : this.playlistQueue.Count;
+
+ public void PlayistRemoveEntry(WorkoutPlaylist playlist, string filePath)
+ {
+ string withoutExtension = Path.GetFileNameWithoutExtension(filePath);
+ for(int index = 0; index < playlist.songs.Count; ++index)
+ {
+ if(playlist.songs[index].trackDataName == withoutExtension)
+ {
+ playlist.RemoveSong(index);
+ this.ExportPlaylistJson(playlist);
+ break;
+ }
+ }
+ }
+
+ public WorkoutPlaylist AddNewPlaylist(string name = "")
+ {
+ if(this.workoutPlaylists == null)
+ this.workoutPlaylists = new List();
+ if(name == "")
+ name = this.GeneratePlaylistName();
+ WorkoutPlaylist workoutPlaylist = new WorkoutPlaylist()
+ {
+ definition = new WorkoutInfo()
+ };
+ workoutPlaylist.definition.workoutName = name;
+ workoutPlaylist.definition.originalName = name;
+ workoutPlaylist.definition.workoutType = WorkoutType.BoxVR_Playlist;
+ this.workoutPlaylists.Add(workoutPlaylist);
+ return workoutPlaylist;
+ }
+
+ private string GeneratePlaylistName()
+ {
+ string str = "playlist";
+ string id = str;
+ int num = 1;
+ while(this.IsIdInPlaylists(id))
+ {
+ id = str + " " + num.ToString();
+ ++num;
+ }
+ return id;
+ }
+
+ private bool IsIdInPlaylists(string id)
+ {
+ foreach(WorkoutPlaylist workoutPlaylist in this.workoutPlaylists)
+ {
+ if(workoutPlaylist.definition.workoutName == id)
+ return true;
+ }
+ return false;
+ }
+
+ public WorkoutPlaylist GetPlaylistByName(string name)
+ {
+ if(this.workoutPlaylists != null)
+ {
+ for(int index = 0; index < this.workoutPlaylists.Count; ++index)
+ {
+ if(this.workoutPlaylists[index].definition.workoutName == name)
+ return this.workoutPlaylists[index];
+ }
+ }
+ return (WorkoutPlaylist)null;
+ }
+
+ public void ClearOrphanedFile(string playlistName)
+ {
+ File.Delete(Path.Combine(this.playlistsPath, playlistName + ".play"));
+ }
+
+ public void ShufflePlaylistTracks(WorkoutPlaylist playlist) => playlist.songs.Shuffle();
+
+ public List GetAllOldSaves()
+ {
+ string path = Path.Combine(Paths.PersistentDataPath, "Playlists");
+ return !Directory.Exists(path) ? new List() : new List((IEnumerable)Tools.IO.GetVisibleFiles(path, "*.playlist.txt"));
+ }
+
+ public List ReturnSongPathsFromOldSavePath(string SaveFilePath)
+ {
+ List stringList = new List();
+ foreach(string readAllLine in File.ReadAllLines(SaveFilePath))
+ {
+ if(File.Exists(readAllLine))
+ stringList.Add(readAllLine);
+ }
+ return stringList;
+ }
+
+ private void ConvertLegacyPlaylistsToGen3()
+ {
+ }
+
+ public class PlaylistToAdd
+ {
+ public WorkoutPlaylist playlistToAdd;
+ public SongDefinition songToAdd;
+ public string originalFilePath;
+ public LocationMode location;
+ }
+
+ public delegate void OnTrackAdditionComplete();
+ }
+}
\ No newline at end of file
diff --git a/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/Segment.cs b/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/Segment.cs
new file mode 100644
index 0000000..4867635
--- /dev/null
+++ b/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/Segment.cs
@@ -0,0 +1,42 @@
+using System.Text;
+
+namespace BoxVRPlaylistManagerNETCore.FitXr.BeatStructure
+{
+ public class Segment
+ {
+ public float _startTime;
+ public int _startBeatIndex;
+ public float _length;
+ public int _numBeats;
+ public float _averageEnergy;
+ public int _index;
+ public Segment.EnergyLevel _energyLevel;
+
+ public Segment(float startTime, float length)
+ {
+ this._startTime = startTime;
+ this._length = length;
+ }
+
+ public float endTime => this._startTime + this._length;
+
+ public override string ToString()
+ {
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.Append("Segment:");
+ stringBuilder.AppendFormat("[{0, 6:F2} Start]", (object)this._startTime);
+ stringBuilder.AppendFormat("[{0, 6:F2} Energy]", (object)this._averageEnergy);
+ stringBuilder.AppendFormat("[{0, 6:F2} Range]", (object)this._energyLevel.ToString());
+ return stringBuilder.ToString();
+ }
+
+ public enum EnergyLevel
+ {
+ UltraLow,
+ Low,
+ Medium,
+ High,
+ Size,
+ }
+ }
+}
diff --git a/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/SegmentList.cs b/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/SegmentList.cs
new file mode 100644
index 0000000..7748be0
--- /dev/null
+++ b/BoxVRPlaylistManagerNETCore/FitXr/BeatStructure/SegmentList.cs
@@ -0,0 +1,139 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using log4net;
+
+namespace BoxVRPlaylistManagerNETCore.FitXr.BeatStructure
+{
+ public class SegmentList
+ {
+ public ILog _log = LogManager.GetLogger(typeof(SegmentList));
+ public List _segments;
+
+ public SegmentList(string[] csvLines, List beats, BarList bars)
+ {
+ this._segments = new List();
+ foreach(string csvLine in csvLines)
+ {
+ List stringList = new List((IEnumerable)csvLine.Split(','));
+ int num = stringList.Count - 1;
+ if(num < 4)
+ {
+ _log.Error(("Not enough entries in line " + csvLine));
+ }
+ this._segments.Add(new Segment(float.Parse(stringList[num - 3]), float.Parse(stringList[num - 2])));
+ }
+ this.AlignSegmentsWithBeats(ref this._segments, beats, bars);
+ this.CalculateSegmentEnergies(ref this._segments, beats);
+ }
+
+ private void CalculateSegmentEnergies(ref List segments, List