TimelineEditorUserControl.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  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. var rects = new List<Tuple<RectangleF, byte>>();
  136. for (var i = beginBlockIndex; i < endBlockIndex; i++)
  137. {
  138. var posX = i * blockWidth;
  139. var block = _spectrumData[i];
  140. var freqArray = block.FreqAxis.Where(p => p > MinSaFreq && p < MaxSaFreq).ToArray();
  141. var height = scaleHeight / freqArray.Length;
  142. foreach (var freq in freqArray)
  143. {
  144. var val = block.Values[Array.IndexOf(block.FreqAxis, freq)];
  145. var posY = scaleHeight * (1 - (freq - MinSaFreq) / freqRange);
  146. if (val < MinSaValue) continue;
  147. byte color;
  148. if (val > MaxSaValue) color = 255;
  149. else
  150. {
  151. var ratio = (val - MinSaValue) / valueRange;
  152. color = (byte)(255 * ratio);
  153. }
  154. rects.Add(new Tuple<RectangleF, byte>(new RectangleF(posX, (float)posY, blockWidth, height), color));
  155. }
  156. }
  157. foreach (var item in rects.Where(p => p != null).GroupBy(p => p.Item2).Where(p => p.Key > 0))
  158. {
  159. using Brush brush = new SolidBrush(Color.FromArgb(255 - item.Key, 255 - item.Key, 255));
  160. e.Graphics.FillRectangles(brush, item.Select(p => p.Item1).ToArray());
  161. }
  162. }
  163. foreach (var item in Items)
  164. {
  165. item.Box.X = (int)(item.TimeMs * DisplayScale);
  166. item.Box.Width = (int)(item.DurationMs * DisplayScale);
  167. item.Box.Height = 50;
  168. e.Graphics.DrawRectangle(Pens.Black, item.Box);
  169. var ts = TimeSpan.FromMilliseconds(item.TimeMs);
  170. var tw = TimeSpan.FromMilliseconds(item.DurationMs);
  171. var state = e.Graphics.Save();
  172. e.Graphics.TranslateTransform(5, 5);
  173. e.Graphics.DrawString($"{item.Text}" +
  174. $"{Environment.NewLine}{tw.TotalMilliseconds:N0}" +
  175. $"{Environment.NewLine}{ts.Minutes:00}:{ts.Seconds:00}.{ts.Milliseconds:000}", Font, Brushes.Black, item.Box.Location);
  176. e.Graphics.Restore(state);
  177. item.Hit.Y = (int)offsetTop;
  178. item.Hit.X = (int)(item.Box.Left + offsetLeft);
  179. item.Hit.Width = item.Box.Width;
  180. item.Hit.Height = item.Box.Height;
  181. }
  182. e.Graphics.ResetTransform();
  183. e.Graphics.DrawString(strPos, Font, Brushes.Black, mid - strPosSize.Width / 2, 0);
  184. e.Graphics.DrawLine(Pens.Red, mid, strPosSize.Height, mid, Height);
  185. }
  186. private void EditorUserControl_MouseMove(object sender, MouseEventArgs e)
  187. {
  188. if (!_downPoint.HasValue)
  189. {
  190. if (LockDown)
  191. {
  192. Cursor = Cursors.Default;
  193. _hoverSide = EditSide.None;
  194. }
  195. else
  196. {
  197. LyricDetailExt hover = null;
  198. foreach (var item in Items)
  199. {
  200. var hBox = item.Hit;
  201. if (e.Y >= hBox.Top && hBox.Bottom >= e.Y && e.X > hBox.Right - Grab && e.X < hBox.Right + Grab)
  202. {
  203. Cursor = Cursors.SizeWE;
  204. _hoverSide = EditSide.Right;
  205. hover = item;
  206. break;
  207. }
  208. if (hBox.Contains(e.Location))
  209. {
  210. Cursor = Cursors.SizeAll;
  211. _hoverSide = EditSide.Body;
  212. hover = item;
  213. break;
  214. }
  215. }
  216. if (hover != null)
  217. {
  218. _hoverObj = hover;
  219. }
  220. else
  221. {
  222. Cursor = Cursors.Default;
  223. _hoverSide = EditSide.None;
  224. _hoverObj = null;
  225. }
  226. }
  227. }
  228. else //drag ing
  229. {
  230. var hd = _downPoint.Value.X - e.X;
  231. switch (_hoverSide)
  232. {
  233. case EditSide.None:
  234. PlayPos = (int)(_lastValue + hd / DisplayScale);
  235. if (PlayPos < 0) PlayPos = 0;
  236. OnPosChange(PlayPos);
  237. break;
  238. case EditSide.Right:
  239. _hoverObj.DurationMs = (int)(_lastValue - hd / DisplayScale);
  240. if (_hoverObj.DurationMs < 10) _hoverObj.DurationMs = 10;
  241. break;
  242. case EditSide.Body:
  243. _hoverObj.TimeMs = (int)(_lastValue - hd / DisplayScale);
  244. break;
  245. default:
  246. throw new ArgumentOutOfRangeException();
  247. }
  248. Invalidate();
  249. }
  250. }
  251. private void EditorUserControl_MouseDown(object sender, MouseEventArgs e)
  252. {
  253. if (e.Button == MouseButtons.Right)
  254. {
  255. var ctx = new ContextMenuStrip();
  256. if (_hoverObj != null)
  257. {
  258. ctx.Items.Add("Set Start").Click += delegate { _hoverObj.TimeMs = PlayPos; };
  259. ctx.Items.Add("Set End").Click += delegate { _hoverObj.DurationMs = PlayPos - _hoverObj.TimeMs; };
  260. ctx.Items.Add(new ToolStripSeparator());
  261. ctx.Items.Add("Set Start Follow All").Click += delegate
  262. {
  263. var moveMs = _hoverObj.TimeMs - PlayPos;
  264. var toMove = Items.Where(p => p.TimeMs >= _hoverObj.TimeMs).ToArray();
  265. foreach (var item in toMove)
  266. {
  267. item.TimeMs -= moveMs;
  268. }
  269. };
  270. ctx.Items.Add(new ToolStripSeparator());
  271. if (_hoverObj == GetPlayItem())
  272. {
  273. ctx.Items.Add("Split...").Click += delegate
  274. {
  275. new SplitForm(Items, _hoverObj, PlayPos).ShowDialog(this);
  276. Invalidate();
  277. };
  278. ctx.Items.Add(new ToolStripSeparator());
  279. }
  280. ctx.Items.Add("Remove").Click += delegate { Items.Remove(_hoverObj); };
  281. }
  282. else
  283. {
  284. ctx.Items.Add("Add").Click += delegate
  285. {
  286. ContextDialog.PopTextBox(MousePosition, "new", (result, s) =>
  287. {
  288. if (result == DialogResult.OK)
  289. {
  290. Items.Add(new LyricDetailExt
  291. {
  292. TimeMs = (int)(e.X / DisplayScale + PlayPos - Width / 2f / DisplayScale),
  293. DurationMs = 1000,
  294. Text = s,
  295. Box =
  296. {
  297. Height = 50,
  298. }
  299. });
  300. Items = Items.OrderBy(p => p.TimeMs).ToList();
  301. Invalidate();
  302. }
  303. });
  304. };
  305. }
  306. ctx.ItemClicked += delegate
  307. {
  308. Invalidate();
  309. };
  310. ctx.Show(MousePosition);
  311. return;
  312. }
  313. _downPoint = e.Location;
  314. switch (_hoverSide)
  315. {
  316. case EditSide.None:
  317. _lastValue = PlayPos;
  318. OnPosChangeBegin();
  319. break;
  320. case EditSide.Body:
  321. _lastValue = _hoverObj.TimeMs;
  322. break;
  323. case EditSide.Right:
  324. _lastValue = _hoverObj.DurationMs;
  325. break;
  326. default:
  327. throw new ArgumentOutOfRangeException();
  328. }
  329. }
  330. private void EditorUserControl_MouseDoubleClick(object sender, MouseEventArgs e)
  331. {
  332. if (e.Button == MouseButtons.Left)
  333. {
  334. if (_hoverObj != null)
  335. {
  336. var localVar = _hoverObj;
  337. ContextDialog.PopTextBox(PointToScreen(localVar.Hit.Location), localVar.Text, (result, s) =>
  338. {
  339. if (result == DialogResult.OK)
  340. {
  341. localVar.Text = s;
  342. Invalidate();
  343. }
  344. });
  345. }
  346. }
  347. }
  348. private void EditorUserControl_MouseUp(object sender, MouseEventArgs e)
  349. {
  350. if (_hoverSide == EditSide.None) OnPosChangeEnd();
  351. _downPoint = null;
  352. _lastValue = 0;
  353. }
  354. protected virtual void OnPosChange(int e)
  355. {
  356. PosChange?.Invoke(this, e);
  357. }
  358. protected virtual void OnPosChangeBegin()
  359. {
  360. PosChangeBegin?.Invoke(this, EventArgs.Empty);
  361. }
  362. protected virtual void OnPosChangeEnd()
  363. {
  364. PosChangeEnd?.Invoke(this, EventArgs.Empty);
  365. }
  366. }
  367. }