TimelineEditorUserControl.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. using BeatLyrics.Tool.Dialogs;
  2. using BeatLyrics.Tool.Models;
  3. using BeatLyrics.Tool.Utils;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.ComponentModel;
  7. using System.Drawing;
  8. using System.Linq;
  9. using System.Windows.Forms;
  10. namespace BeatLyrics.Tool.UserControls
  11. {
  12. internal partial class EditorUserControl : UserControl
  13. {
  14. private const int Grab = 3;
  15. private LyricDetailExt _hoverObj;
  16. private EditSide _hoverSide;
  17. private Point? _downPoint;
  18. private int _lastValue;
  19. private List<LyricDetailExt> _items = new List<LyricDetailExt>();
  20. private bool _lockDown;
  21. private int _playPos;
  22. private float _displayScale = 0.1f;
  23. private SpectrumAnalyzer.SaResult[] _spectrumData;
  24. private int _spectrumBlockInMs;
  25. private enum EditSide
  26. {
  27. None,
  28. Right,
  29. Body,
  30. }
  31. public float DisplayScale
  32. {
  33. get => _displayScale;
  34. set
  35. {
  36. _displayScale = value;
  37. Invalidate();
  38. }
  39. }
  40. public int PlayPos
  41. {
  42. get => _playPos;
  43. set
  44. {
  45. _playPos = value;
  46. Invalidate();
  47. }
  48. }
  49. [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  50. public List<LyricDetailExt> Items
  51. {
  52. get => _items;
  53. set
  54. {
  55. _items = value;
  56. Invalidate();
  57. }
  58. }
  59. public bool LockDown
  60. {
  61. get => _lockDown;
  62. set
  63. {
  64. _lockDown = value;
  65. Invalidate();
  66. }
  67. }
  68. public double MinSaFreq { get; set; } = 80;
  69. public double MaxSaFreq { get; set; } = 2000;
  70. public double MinSaValue { get; set; } = -40;
  71. public double MaxSaValue { get; set; } = 0;
  72. public event EventHandler PosChangeBegin;
  73. public event EventHandler<int> PosChange;
  74. public event EventHandler PosChangeEnd;
  75. public LyricDetailExt GetPlayItem()
  76. {
  77. foreach (var item in Items)
  78. {
  79. if (item.TimeMs <= PlayPos && item.TimeMs + item.DurationMs > PlayPos) return item;
  80. }
  81. return null;
  82. }
  83. public string GetPlayText() => GetPlayItem()?.Text ?? "";
  84. public void SetSpectrumData(SpectrumAnalyzer.SaResult[] sa, int blockInMs)
  85. {
  86. _spectrumData = sa;
  87. _spectrumBlockInMs = blockInMs;
  88. Invalidate();
  89. }
  90. public EditorUserControl()
  91. {
  92. DoubleBuffered = true;
  93. InitializeComponent();
  94. }
  95. protected override void OnCreateControl()
  96. {
  97. base.OnCreateControl();
  98. if (DesignMode)
  99. {
  100. Items = new List<LyricDetailExt>
  101. {
  102. new LyricDetailExt { TimeMs = 0, DurationMs = 250, Text = "Abc"},
  103. new LyricDetailExt { TimeMs = 500, DurationMs = 500, Text = "Def"},
  104. new LyricDetailExt { TimeMs = 2000, DurationMs = 1000, Text = "Ghi"}
  105. };
  106. }
  107. }
  108. protected override void OnResize(EventArgs e)
  109. {
  110. base.OnResize(e);
  111. Invalidate();
  112. }
  113. private void EditorUserControl_Paint(object sender, PaintEventArgs e)
  114. {
  115. e.Graphics.ResetTransform();
  116. e.Graphics.Clear(LockDown ? Color.FromKnownColor(KnownColor.Control) : Color.White);
  117. var mid = Width / 2;
  118. var t = TimeSpan.FromMilliseconds(Math.Abs(PlayPos));
  119. var strPos = PlayPos < 0 ? $"-{t.Minutes:00}:{t.Seconds:00}.{t.Milliseconds:000}" : $"{t.Minutes:00}:{t.Seconds:00}.{t.Milliseconds:000}";
  120. var strPosSize = e.Graphics.MeasureString(strPos, Font);
  121. var offsetLeft = -PlayPos * DisplayScale + mid;
  122. var offsetTop = strPosSize.Height;
  123. e.Graphics.TranslateTransform(offsetLeft, offsetTop);
  124. if (_spectrumData != null) // Draw Spectrum background
  125. {
  126. var msDurHalf = Width / DisplayScale / 2;
  127. var beginBlockIndex = (int)(PlayPos - msDurHalf) / _spectrumBlockInMs;
  128. var endBlockIndex = (int)(PlayPos + msDurHalf) / _spectrumBlockInMs;
  129. if (beginBlockIndex < 0) beginBlockIndex = 0;
  130. if (endBlockIndex > _spectrumData.Length) endBlockIndex = _spectrumData.Length;
  131. var scaleHeight = Height - offsetTop;
  132. var valueRange = MaxSaValue - MinSaValue;
  133. var freqRange = MaxSaFreq - MinSaFreq;
  134. var blockWidth = _spectrumBlockInMs * DisplayScale;
  135. for (var i = beginBlockIndex; i < endBlockIndex; i++)
  136. {
  137. var posX = i * blockWidth;
  138. var block = _spectrumData[i];
  139. var freqArray = block.FreqAxis.Where(p => p > MinSaFreq && p < MaxSaFreq).ToArray();
  140. var height = scaleHeight / freqArray.Length;
  141. var rects = new Tuple<RectangleF, byte>[freqArray.Length];
  142. for (var index = 0; index < freqArray.Length; index++)
  143. {
  144. var freq = freqArray[index];
  145. var val = block.Values[Array.IndexOf(block.FreqAxis, freq)];
  146. var posY = scaleHeight * (1 - (freq - MinSaFreq) / freqRange);
  147. if (val < MinSaValue) continue;
  148. byte color;
  149. if (val > MaxSaValue) color = 255;
  150. else
  151. {
  152. var ratio = (val - MinSaValue) / valueRange;
  153. color = (byte)(255 * ratio);
  154. }
  155. rects[index] = new Tuple<RectangleF, byte>(
  156. new RectangleF(posX, (float)posY, blockWidth, height), color
  157. );
  158. }
  159. foreach (var item in rects.Where(p => p != null).GroupBy(p => p.Item2).Where(p => p.Key > 0))
  160. {
  161. using Brush brush = new SolidBrush(Color.FromArgb(255 - item.Key, 255 - item.Key, 255));
  162. e.Graphics.FillRectangles(brush, item.Select(p => p.Item1).ToArray());
  163. }
  164. }
  165. }
  166. foreach (var item in Items)
  167. {
  168. item.Box.X = (int)(item.TimeMs * DisplayScale);
  169. item.Box.Width = (int)(item.DurationMs * DisplayScale);
  170. item.Box.Height = 50;
  171. e.Graphics.DrawRectangle(Pens.Black, item.Box);
  172. var ts = TimeSpan.FromMilliseconds(item.TimeMs);
  173. var tw = TimeSpan.FromMilliseconds(item.DurationMs);
  174. var state = e.Graphics.Save();
  175. e.Graphics.TranslateTransform(5, 5);
  176. e.Graphics.DrawString($"{item.Text}" +
  177. $"{Environment.NewLine}{tw.TotalMilliseconds:N0}" +
  178. $"{Environment.NewLine}{ts.Minutes:00}:{ts.Seconds:00}.{ts.Milliseconds:000}", Font, Brushes.Black, item.Box.Location);
  179. e.Graphics.Restore(state);
  180. item.Hit.Y = (int)offsetTop;
  181. item.Hit.X = (int)(item.Box.Left + offsetLeft);
  182. item.Hit.Width = item.Box.Width;
  183. item.Hit.Height = item.Box.Height;
  184. }
  185. e.Graphics.ResetTransform();
  186. e.Graphics.DrawString(strPos, Font, Brushes.Black, mid - strPosSize.Width / 2, 0);
  187. e.Graphics.DrawLine(Pens.Red, mid, strPosSize.Height, mid, Height);
  188. }
  189. private void EditorUserControl_MouseMove(object sender, MouseEventArgs e)
  190. {
  191. if (!_downPoint.HasValue)
  192. {
  193. if (LockDown)
  194. {
  195. Cursor = Cursors.Default;
  196. _hoverSide = EditSide.None;
  197. }
  198. else
  199. {
  200. LyricDetailExt hover = null;
  201. foreach (var item in Items)
  202. {
  203. var hBox = item.Hit;
  204. if (e.Y >= hBox.Top && hBox.Bottom >= e.Y && e.X > hBox.Right - Grab && e.X < hBox.Right + Grab)
  205. {
  206. Cursor = Cursors.SizeWE;
  207. _hoverSide = EditSide.Right;
  208. hover = item;
  209. break;
  210. }
  211. if (hBox.Contains(e.Location))
  212. {
  213. Cursor = Cursors.SizeAll;
  214. _hoverSide = EditSide.Body;
  215. hover = item;
  216. break;
  217. }
  218. }
  219. if (hover != null)
  220. {
  221. _hoverObj = hover;
  222. }
  223. else
  224. {
  225. Cursor = Cursors.Default;
  226. _hoverSide = EditSide.None;
  227. _hoverObj = null;
  228. }
  229. }
  230. }
  231. else //drag ing
  232. {
  233. var hd = _downPoint.Value.X - e.X;
  234. switch (_hoverSide)
  235. {
  236. case EditSide.None:
  237. PlayPos = (int)(_lastValue + hd / DisplayScale);
  238. if (PlayPos < 0) PlayPos = 0;
  239. OnPosChange(PlayPos);
  240. break;
  241. case EditSide.Right:
  242. _hoverObj.DurationMs = (int)(_lastValue - hd / DisplayScale);
  243. if (_hoverObj.DurationMs < 10) _hoverObj.DurationMs = 10;
  244. break;
  245. case EditSide.Body:
  246. _hoverObj.TimeMs = (int)(_lastValue - hd / DisplayScale);
  247. break;
  248. default:
  249. throw new ArgumentOutOfRangeException();
  250. }
  251. Invalidate();
  252. }
  253. }
  254. private void EditorUserControl_MouseDown(object sender, MouseEventArgs e)
  255. {
  256. if (e.Button == MouseButtons.Right)
  257. {
  258. var ctx = new ContextMenuStrip();
  259. if (_hoverObj != null)
  260. {
  261. ctx.Items.Add("Set Start").Click += delegate { _hoverObj.TimeMs = PlayPos; };
  262. ctx.Items.Add("Set End").Click += delegate { _hoverObj.DurationMs = PlayPos - _hoverObj.TimeMs; };
  263. ctx.Items.Add(new ToolStripSeparator());
  264. ctx.Items.Add("Set Start Follow All").Click += delegate
  265. {
  266. var moveMs = _hoverObj.TimeMs - PlayPos;
  267. var toMove = Items.Where(p => p.TimeMs >= _hoverObj.TimeMs).ToArray();
  268. foreach (var item in toMove)
  269. {
  270. item.TimeMs -= moveMs;
  271. }
  272. };
  273. ctx.Items.Add(new ToolStripSeparator());
  274. if (_hoverObj == GetPlayItem())
  275. {
  276. ctx.Items.Add("Split...").Click += delegate
  277. {
  278. new SplitForm(Items, _hoverObj, PlayPos).ShowDialog(this);
  279. Invalidate();
  280. };
  281. ctx.Items.Add(new ToolStripSeparator());
  282. }
  283. ctx.Items.Add("Remove").Click += delegate { Items.Remove(_hoverObj); };
  284. }
  285. else
  286. {
  287. ctx.Items.Add("Add").Click += delegate
  288. {
  289. ContextDialog.PopTextBox(MousePosition, "new", (result, s) =>
  290. {
  291. if (result == DialogResult.OK)
  292. {
  293. Items.Add(new LyricDetailExt
  294. {
  295. TimeMs = (int)(e.X / DisplayScale + PlayPos - Width / 2f / DisplayScale),
  296. DurationMs = 1000,
  297. Text = s,
  298. Box =
  299. {
  300. Height = 50,
  301. }
  302. });
  303. Items = Items.OrderBy(p => p.TimeMs).ToList();
  304. Invalidate();
  305. }
  306. });
  307. };
  308. }
  309. ctx.ItemClicked += delegate
  310. {
  311. Invalidate();
  312. };
  313. ctx.Show(MousePosition);
  314. return;
  315. }
  316. _downPoint = e.Location;
  317. switch (_hoverSide)
  318. {
  319. case EditSide.None:
  320. _lastValue = PlayPos;
  321. OnPosChangeBegin();
  322. break;
  323. case EditSide.Body:
  324. _lastValue = _hoverObj.TimeMs;
  325. break;
  326. case EditSide.Right:
  327. _lastValue = _hoverObj.DurationMs;
  328. break;
  329. default:
  330. throw new ArgumentOutOfRangeException();
  331. }
  332. }
  333. private void EditorUserControl_MouseDoubleClick(object sender, MouseEventArgs e)
  334. {
  335. if (e.Button == MouseButtons.Left)
  336. {
  337. if (_hoverObj != null)
  338. {
  339. var localVar = _hoverObj;
  340. ContextDialog.PopTextBox(PointToScreen(localVar.Hit.Location), localVar.Text, (result, s) =>
  341. {
  342. if (result == DialogResult.OK)
  343. {
  344. localVar.Text = s;
  345. Invalidate();
  346. }
  347. });
  348. }
  349. }
  350. }
  351. private void EditorUserControl_MouseUp(object sender, MouseEventArgs e)
  352. {
  353. if (_hoverSide == EditSide.None) OnPosChangeEnd();
  354. _downPoint = null;
  355. _lastValue = 0;
  356. }
  357. protected virtual void OnPosChange(int e)
  358. {
  359. PosChange?.Invoke(this, e);
  360. }
  361. protected virtual void OnPosChangeBegin()
  362. {
  363. PosChangeBegin?.Invoke(this, EventArgs.Empty);
  364. }
  365. protected virtual void OnPosChangeEnd()
  366. {
  367. PosChangeEnd?.Invoke(this, EventArgs.Empty);
  368. }
  369. }
  370. }