MainForm.cs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745
  1. using BsWidget.BeatSaberHttpStatus;
  2. using BsWidget.BsYurHttpStatus;
  3. using BsWidgetShareCodes;
  4. using OpenHardwareMonitor.Hardware;
  5. using System;
  6. using System.Collections.Concurrent;
  7. using System.Collections.Generic;
  8. using System.Drawing;
  9. using System.Drawing.Drawing2D;
  10. using System.IO;
  11. using System.Linq;
  12. using System.Net.Http;
  13. using System.Threading.Tasks;
  14. using System.Windows.Forms;
  15. using BsWidget.BSDataPuller;
  16. namespace BsWidget;
  17. internal class MainForm : BaseForm
  18. {
  19. private const float RecordPixel = 2.5f;
  20. private const string FontName = "BIZ UDGothic Bold";
  21. private Timer _updateTimer;
  22. private Timer _pcTimer;
  23. private BlockingCollection<UpdateFlags> _queue;
  24. private BsYurHttpStatusClient _yurClient;
  25. //private BeatSaberHttpStatusClient _client;
  26. private BsDataPullerClient _client;
  27. private Computer _computer;
  28. private Font _smallFont = new Font(FontName, 12, FontStyle.Regular, GraphicsUnit.Pixel);
  29. private Font _mediumFont = new Font(FontName, 20, FontStyle.Regular, GraphicsUnit.Pixel);
  30. [Flags]
  31. private enum UpdateFlags
  32. {
  33. BeatMap = 1 << 0,
  34. Performance = 1 << 1,
  35. NoteFullyCut = 1 << 2,
  36. ClearAll = 1 << 3,
  37. RefreshAll = 1 << 4,
  38. CpuAndGpu = 1 << 5,
  39. }
  40. public Image SongIcon { get; set; } = Properties.Resources.sample_cover;
  41. public string SongName { get; set; } = "Song Name";
  42. public string SongSubName { get; set; } = "Song Sub Name";
  43. public string SongArtist { get; set; } = "Song Artist";
  44. public string BeatMapper { get; set; } = "Mapper";
  45. public string Difficulty { get; set; } = "Difficulty";
  46. public double SongBpm { get; set; } = 123;
  47. public double SongNjs { get; set; } = 23;
  48. public int CurrentScore { get; set; } = 12345;
  49. public int CurrentCombo { get; set; } = 120;
  50. public string CurrentRank { get; set; } = "XX";
  51. public double CurrentAcc { get; set; } = 0.0;
  52. public int CurrentHitsPerSecond { get; set; }
  53. public int CurrentMissed { get; set; }
  54. public float? CurrentCpuUsage { get; set; } = 12.3f;
  55. public float? CurrentCpuGhz { get; set; } = 2.333f;
  56. public float? CurrentGpuUsage { get; set; } = 45.6f;
  57. public YurStatus CurrentYurStatus { get; set; } = new YurStatus { HeartRate = 0, KcalPerMin = 0 };
  58. public int? MaxHitsPerSecond { get; set; }
  59. public float? MaxHeartRate { get; set; }
  60. /// <summary> FinalScore,CurrentCombo </summary>
  61. public List<HistoryModel> CutHistory { get; set; }
  62. public class HistoryModel
  63. {
  64. public DateTime Time { get; set; }
  65. public int HitScore { get; set; }
  66. public int CurrentCombo { get; set; }
  67. public float Acc { get; set; }
  68. public int HitsPerSecond { get; set; }
  69. public float HeartRate { get; set; }
  70. public float KcalPerMin { get; set; }
  71. public int ComboDelta { get; set; }
  72. }
  73. public MainForm()
  74. {
  75. KeyPreview = true;
  76. FormBorderStyle = FormBorderStyle.None;
  77. //_client = new BeatSaberHttpStatusClient();
  78. _client = new BsDataPullerClient();
  79. //_client.Event += BeatSaber_Event;
  80. _client.Event += BsDataPuller_Event;
  81. _yurClient = new BsYurHttpStatusClient();
  82. _yurClient.Event += Yur_Event;
  83. if (Program.NoPerf)
  84. {
  85. }
  86. else
  87. {
  88. _computer = new Computer();
  89. _computer.CPUEnabled = true;
  90. _computer.GPUEnabled = true;
  91. _computer.FanControllerEnabled = false;
  92. _computer.HDDEnabled = false;
  93. _computer.MainboardEnabled = false;
  94. _computer.RAMEnabled = false;
  95. _computer.Open();
  96. _pcTimer = new Timer { Interval = 750 };
  97. _pcTimer.Tick += PcTimer_Tick;
  98. }
  99. _queue = new BlockingCollection<UpdateFlags>();
  100. CutHistory = new List<HistoryModel>();
  101. _updateTimer = new Timer { Interval = 25 };
  102. _updateTimer.Tick += UpdateTimer_Tick;
  103. }
  104. protected override void OnLoad(EventArgs e)
  105. {
  106. Text = "Beat Saber Status Widget";
  107. base.OnLoad(e);
  108. Font = new Font(FontName, 30, FontStyle.Bold, GraphicsUnit.Pixel);
  109. TopMost = false;
  110. WindowState = FormWindowState.Normal;
  111. _updateTimer.Start();
  112. _client.Start();
  113. _yurClient.Start();
  114. _pcTimer?.Start();
  115. #if DEBUG
  116. Task.Run(Debugging);
  117. #endif
  118. }
  119. private async void Debugging()
  120. {
  121. lock (CutHistory)
  122. {
  123. CutHistory.Add(new HistoryModel { HeartRate = 30, KcalPerMin = 30 });
  124. CutHistory.Add(new HistoryModel { HeartRate = 1, KcalPerMin = 1 });
  125. }
  126. var rng = new Random();
  127. while (Visible)
  128. {
  129. lock (CutHistory)
  130. {
  131. if (CutHistory.Count > (Width / RecordPixel) * 2) CutHistory.RemoveRange(0, CutHistory.Count / 2);
  132. var historyModel = new HistoryModel
  133. {
  134. HitsPerSecond = rng.Next(15),
  135. HeartRate = 15 + rng.Next(2),
  136. KcalPerMin = 15 + rng.Next(2),
  137. };
  138. CutHistory.Add(historyModel);
  139. CutHistory.Add(historyModel);
  140. //CutHistory.Add(historyModel);
  141. }
  142. _queue.Add(UpdateFlags.NoteFullyCut);
  143. await Task.Delay(1000 / 15);
  144. }
  145. }
  146. protected override void OnMouseDoubleClick(MouseEventArgs e)
  147. {
  148. base.OnMouseDoubleClick(e);
  149. #if DEBUG
  150. _queue.Add(UpdateFlags.RefreshAll);
  151. #else
  152. _queue.Add(UpdateFlags.ClearAll);
  153. #endif
  154. }
  155. protected override void OnShown(EventArgs e)
  156. {
  157. WindowState = FormWindowState.Normal;
  158. Location = Screen.PrimaryScreen.Bounds.Location;
  159. ClientSize = Screen.PrimaryScreen.Bounds.Size;
  160. TopMost = true;
  161. base.OnShown(e);
  162. if (_queue.Count == 0)
  163. {
  164. _queue.Add(UpdateFlags.RefreshAll);
  165. }
  166. }
  167. protected override void OnFormClosing(FormClosingEventArgs e)
  168. {
  169. _updateTimer.Stop();
  170. _pcTimer?.Stop();
  171. base.OnFormClosing(e);
  172. }
  173. protected override void OnFormClosed(FormClosedEventArgs e)
  174. {
  175. _client.Stop();
  176. _yurClient.Stop();
  177. _computer?.Close();
  178. base.OnFormClosed(e);
  179. }
  180. protected override void OnKeyDown(KeyEventArgs e)
  181. {
  182. base.OnKeyDown(e);
  183. if (e.KeyCode == Keys.Escape) Application.Exit();
  184. }
  185. private void BeatSaber_Event(object sender, BeatSaberStatusEventArgs e)
  186. {
  187. var flags = (UpdateFlags)0;
  188. if (e.Event == "menu" && CutHistory.Count > 0)
  189. {
  190. lock (CutHistory)
  191. {
  192. CutHistory.Clear();
  193. }
  194. flags |= UpdateFlags.ClearAll;
  195. CurrentMissed = 0;
  196. }
  197. if (null != e.Status.Beatmap)
  198. {
  199. CurrentMissed = 0;
  200. var bytes = Convert.FromBase64String(e.Status.Beatmap.SongCover);
  201. using var stream = new MemoryStream(bytes);
  202. var newImg = Image.FromStream(stream);
  203. var old = SongIcon;
  204. SongIcon = newImg;
  205. old?.Dispose();
  206. SongName = e.Status.Beatmap.SongName;
  207. SongSubName = e.Status.Beatmap.SongSubName;
  208. SongArtist = e.Status.Beatmap.SongAuthorName;
  209. BeatMapper = e.Status.Beatmap.LevelAuthorName;
  210. if (string.IsNullOrEmpty(BeatMapper)) BeatMapper = "Unknown Mapper";
  211. Difficulty = e.Status.Beatmap.Difficulty.ToUpper();
  212. if (Difficulty == "EXPERTPLUS") Difficulty = "EXPERT+";
  213. SongBpm = e.Status.Beatmap.SongBPM;
  214. SongNjs = e.Status.Beatmap.NoteJumpSpeed;
  215. flags |= UpdateFlags.BeatMap;
  216. }
  217. if (null != e.Status.Performance)
  218. {
  219. CurrentScore = e.Status.Performance.Score;
  220. CurrentCombo = e.Status.Performance.Combo;
  221. CurrentRank = e.Status.Performance.Rank;
  222. //CurrentMaxScore = e.Status.Performance.CurrentMaxScore;
  223. flags |= UpdateFlags.Performance;
  224. }
  225. if (e.Event == "noteMissed")
  226. {
  227. ++CurrentMissed;
  228. flags |= UpdateFlags.Performance;
  229. }
  230. if (e.Event == "noteFullyCut" && null != e.NoteCut?.FinalScore)
  231. {
  232. lock (CutHistory)
  233. {
  234. var now = DateTime.Now;
  235. var preNow = now.AddSeconds(-1);
  236. CurrentHitsPerSecond = CutHistory.Count(p => p.Time > preNow);
  237. CutHistory.Add(new HistoryModel
  238. {
  239. Time = now,
  240. HitScore = e.NoteCut.FinalScore.Value,
  241. CurrentCombo = CurrentCombo,
  242. //Percent = CurrentMaxScore == 0 ? 0 : ((float)CurrentScore / CurrentMaxScore),
  243. HitsPerSecond = CurrentHitsPerSecond,
  244. HeartRate = CurrentYurStatus.HeartRate,
  245. KcalPerMin = CurrentYurStatus.KcalPerMin,
  246. });
  247. }
  248. flags |= UpdateFlags.NoteFullyCut;
  249. }
  250. if (flags == 0)
  251. {
  252. }
  253. else
  254. {
  255. _queue.Add(flags);
  256. }
  257. }
  258. private void BsDataPuller_Event(object sender, BsDataPullerEventArgs e)
  259. {
  260. if (e.Type == BsDataPullerEventArgsType.LiveData)
  261. {
  262. if (e.LiveData.Score == 0) //new game start
  263. {
  264. CurrentHitsPerSecond = 0;
  265. lock (CutHistory) CutHistory.Clear();
  266. MaxHitsPerSecond = null;
  267. MaxHeartRate = null;
  268. }
  269. var comboDelta = e.LiveData.Combo - CurrentCombo;
  270. if (comboDelta < 0) comboDelta = 0;
  271. CurrentScore = e.LiveData.Score;
  272. CurrentCombo = e.LiveData.Combo;
  273. CurrentRank = e.LiveData.Rank;
  274. CurrentAcc = e.LiveData.Accuracy;
  275. CurrentMissed = e.LiveData.Misses;
  276. lock (CutHistory)
  277. {
  278. var now = DateTime.Now;
  279. var preNow = now.AddSeconds(-1);
  280. CurrentHitsPerSecond = CutHistory.Where(p => p.Time > preNow).Sum(p => p.ComboDelta);
  281. if (CurrentHitsPerSecond > MaxHitsPerSecond || MaxHitsPerSecond.HasValue == false) MaxHitsPerSecond = CurrentHitsPerSecond;
  282. CutHistory.Add(new HistoryModel
  283. {
  284. Time = now,
  285. HitScore = e.LiveData.BlockHitScore.Total,
  286. CurrentCombo = CurrentCombo,
  287. ComboDelta = comboDelta,
  288. Acc = (float)CurrentAcc,
  289. HitsPerSecond = CurrentHitsPerSecond,
  290. HeartRate = CurrentYurStatus.HeartRate,
  291. KcalPerMin = CurrentYurStatus.KcalPerMin,
  292. });
  293. }
  294. _queue.Add(UpdateFlags.Performance | UpdateFlags.NoteFullyCut);
  295. }
  296. if (e.Type == BsDataPullerEventArgsType.MapData)
  297. {
  298. SongName = e.MapData.SongName;
  299. SongSubName = e.MapData.SongSubName;
  300. SongArtist = e.MapData.SongAuthor;
  301. BeatMapper = e.MapData.Mapper;
  302. if (string.IsNullOrEmpty(BeatMapper)) BeatMapper = "Unknown Mapper";
  303. Difficulty = e.MapData.Difficulty.ToUpper();
  304. if (Difficulty == "EXPERTPLUS") Difficulty = "EXPERT+";
  305. if (Difficulty.Length == 0) Difficulty = "???";
  306. SongBpm = e.MapData.BPM;
  307. SongNjs = e.MapData.NJS;
  308. if (e.MapData.CoverImage != null)
  309. {
  310. using var hc = new HttpClient();
  311. var bytes = hc.GetAsync(e.MapData.CoverImage).Result.Content.ReadAsByteArrayAsync().Result;
  312. using var stream = new MemoryStream(bytes);
  313. var newImg = Image.FromStream(stream);
  314. var old = SongIcon;
  315. SongIcon = newImg;
  316. old?.Dispose();
  317. }
  318. else SongIcon = Properties.Resources.sample_cover;
  319. _queue.Add(UpdateFlags.ClearAll | UpdateFlags.RefreshAll);
  320. }
  321. }
  322. private void Yur_Event(object sender, YurStatus e)
  323. {
  324. CurrentYurStatus = e;
  325. if (e.HeartRate > MaxHeartRate || MaxHeartRate.HasValue == false) MaxHeartRate = e.HeartRate;
  326. _queue.Add(UpdateFlags.Performance);
  327. }
  328. private void PcTimer_Tick(object sender, EventArgs e)
  329. {
  330. try
  331. {
  332. foreach (var hardware in _computer.Hardware.Where(p => p.HardwareType == HardwareType.CPU || p.HardwareType == HardwareType.GpuNvidia || p.HardwareType == HardwareType.GpuAti))
  333. {
  334. hardware.Update();
  335. }
  336. }
  337. catch (Exception)
  338. {
  339. return;
  340. }
  341. try
  342. {
  343. var mhz = _computer.Hardware.Where(p => p.HardwareType == HardwareType.CPU).SelectMany(p => p.Sensors)
  344. .Where(p => p.SensorType == SensorType.Clock).Select(p => p.Value).Max();
  345. if (mhz.HasValue)
  346. {
  347. CurrentCpuGhz = mhz / 1000f;
  348. }
  349. else
  350. {
  351. CurrentCpuGhz = null;
  352. }
  353. }
  354. catch
  355. {
  356. CurrentCpuGhz = null;
  357. }
  358. try
  359. {
  360. CurrentCpuUsage = _computer.Hardware.Where(p => p.HardwareType == HardwareType.CPU).SelectMany(p => p.Sensors)
  361. .Where(p => p.SensorType == SensorType.Load && p.Name == "CPU Total").Select(p => p.Value).Max();
  362. }
  363. catch
  364. {
  365. CurrentCpuUsage = null;
  366. }
  367. try
  368. {
  369. CurrentGpuUsage = _computer.Hardware.Where(p => p.HardwareType == HardwareType.GpuNvidia || p.HardwareType == HardwareType.GpuAti).SelectMany(p => p.Sensors)
  370. .Where(p => p.SensorType == SensorType.Load && p.Name == "GPU Core").Select(p => p.Value).Max();
  371. }
  372. catch
  373. {
  374. CurrentGpuUsage = null;
  375. }
  376. _queue.Add(UpdateFlags.CpuAndGpu);
  377. }
  378. private void UpdateTimer_Tick(object sender, EventArgs e)
  379. {
  380. if (_queue.Count == 0) return;
  381. UpdateGraphic();
  382. }
  383. protected override void RenderGraphic(Graphics g)
  384. {
  385. const int margin = 10;
  386. const int chHeight = 64;
  387. var chWidth = ViewSize.Width - margin * 2;
  388. if ((_queue?.Count ?? 0) == 0) return;
  389. var flags = _queue.Take();
  390. if (flags.HasFlag(UpdateFlags.ClearAll)) g.Clear(Color.Transparent);
  391. g.SetHighQuality();
  392. if (flags.HasFlag(UpdateFlags.NoteFullyCut) || flags.HasFlag(UpdateFlags.RefreshAll))
  393. {
  394. const int chLeft = margin;
  395. const int chTop = margin;
  396. var chBottom = chTop + chHeight;
  397. var LineWidth = 1.5f;
  398. var displayCount = (int)(chWidth / RecordPixel);
  399. using var scorePen = new Pen(Color.FromArgb(200, 255, 0, 0), LineWidth) { LineJoin = LineJoin.Round };
  400. using var comboPen = new Pen(Color.FromArgb(200, 0, 0, 255), LineWidth) { LineJoin = LineJoin.Round };
  401. using var accPen = new Pen(Color.FromArgb(200, Color.Purple), LineWidth) { LineJoin = LineJoin.Round };
  402. using var cpsPen = new Pen(Color.Yellow, LineWidth) { LineJoin = LineJoin.Round };
  403. using var calPen = new Pen(Color.FromArgb(200, Color.Lime), LineWidth) { LineJoin = LineJoin.Round, };
  404. using var heartPen = new Pen(Color.White, LineWidth) { LineJoin = LineJoin.Round, DashStyle = DashStyle.Dot };
  405. using var dashPen = new Pen(Color.DarkOrange);
  406. dashPen.DashStyle = DashStyle.Dash;
  407. dashPen.Width = LineWidth * 2;
  408. dashPen.DashOffset = LineWidth * 2;
  409. using var dashPen2 = new Pen(Color.DarkGray);
  410. dashPen2.DashStyle = DashStyle.Dash;
  411. dashPen2.Width = LineWidth * 2;
  412. dashPen2.DashOffset = LineWidth * 2;
  413. //clear region 0,210 Wx200
  414. g.ClearRect(Color.Transparent, 0, chTop - 3, ViewSize.Width, chHeight + 6 + margin * 2);
  415. //draw chart
  416. using var bgBrush = new SolidBrush(Color.FromArgb(90, 255, 255, 255));
  417. g.FillRectangle(bgBrush, chLeft, chTop, chWidth, chHeight);
  418. g.DrawRectangle(Pens.White, chLeft - 1, chTop - 1, chWidth + 2, chHeight + 2);
  419. if (CutHistory.Count > 1)
  420. {
  421. HistoryModel[] items;
  422. lock (CutHistory)
  423. {
  424. var skip = CutHistory.Count > displayCount ? CutHistory.Count - displayCount : 0;
  425. items = CutHistory.Skip(skip).ToArray();
  426. }
  427. if (items.Length > 1)
  428. {
  429. var minScore = items.Select(p => p.HitScore).Min();
  430. var maxScore = items.Select(p => p.HitScore).Max();
  431. var rngScore = maxScore - minScore;
  432. if (rngScore < 1) rngScore = 1;
  433. var minCombo = items.Select(p => p.CurrentCombo).Min();
  434. var maxCombo = items.Select(p => p.CurrentCombo).Max();
  435. var rngCombo = maxCombo - minCombo;
  436. if (rngCombo < 1) rngCombo = 1;
  437. var minHea = items.Select(p => p.HeartRate).Min();
  438. var maxHea = items.Select(p => p.HeartRate).Max();
  439. var rngHea = maxHea - minHea;
  440. if (rngHea < 1) rngHea = 1;
  441. var minCal = items.Select(p => p.KcalPerMin).Min();
  442. var maxCal = items.Select(p => p.KcalPerMin).Max();
  443. var rngCal = maxCal - minCal;
  444. if (rngCal < 1) rngCal = 1;
  445. var minCps = items.Select(p => p.HitsPerSecond).Min();
  446. var maxCps = items.Select(p => p.HitsPerSecond).Max();
  447. var rngCps = maxCps - minCps;
  448. if (rngCps < 1) rngCps = 1;
  449. var baseX = chWidth - items.Length * RecordPixel + chLeft + 1;
  450. var scorePts = new List<PointF>();
  451. var comboPts = new List<PointF>();
  452. var accPts = new List<PointF>();
  453. var heaPts = new List<PointF>();
  454. var calPts = new List<PointF>();
  455. var cpsPts = new List<PointF>();
  456. var peaks = new List<Tuple<int, float>>();
  457. var skipFirst = true;
  458. for (var i = 0; i < items.Length; i++)
  459. {
  460. var item = items[i];
  461. var pointX = baseX + RecordPixel * i;
  462. scorePts.Add(new PointF(pointX, (chBottom - 1) - (chHeight - 2) * ((float)(item.HitScore - minScore) / rngScore)));
  463. comboPts.Add(new PointF(pointX, (chBottom - 1) - (chHeight - 2) * ((float)(item.CurrentCombo - minCombo) / rngCombo)));
  464. accPts.Add(new PointF(pointX, (chBottom - 1) - (chHeight - 2) * item.Acc / 100));
  465. cpsPts.Add(new PointF(pointX, (chBottom - 1) - (chHeight - 2) * ((float)(item.HitsPerSecond - minCps) / rngCps)));
  466. heaPts.Add(new PointF(pointX, (chBottom - 1) - (chHeight - 2) * ((item.HeartRate - minHea) / rngHea)));
  467. calPts.Add(new PointF(pointX, (chBottom - 1) - (chHeight - 2) * ((item.KcalPerMin - minCal) / rngCal)));
  468. if (peaks.Count > 0)
  469. {
  470. var maxVal = peaks.OrderByDescending(p => p.Item1).First();
  471. var topX = peaks.Where(p => p.Item1 == maxVal.Item1).OrderBy(p => p.Item2).Select(p => p.Item2).First();
  472. if (pointX - topX > margin * 2)
  473. {
  474. peaks.Clear();
  475. if (skipFirst)
  476. {
  477. skipFirst = false;
  478. }
  479. else
  480. {
  481. g.DrawLine(dashPen, topX, chTop, topX, chBottom);
  482. g.DrawStringWithRoundedRect(maxVal.Item1 + "", _smallFont, topX - 4, chBottom, Brushes.Black, Brushes.Yellow, margin / 2.0f);
  483. }
  484. }
  485. }
  486. if (i > 0 && i < items.Length - 1)
  487. {
  488. var prevItem = items[i - 1];
  489. var nextItem = items[i + 1];
  490. if (prevItem.HitsPerSecond <= item.HitsPerSecond && item.HitsPerSecond > nextItem.HitsPerSecond)
  491. {
  492. //peak detected
  493. if (peaks.Count > 0)
  494. {
  495. if (peaks.Max(p => p.Item1) < item.HitsPerSecond)
  496. {
  497. peaks.Add(new Tuple<int, float>(item.HitsPerSecond, pointX));
  498. //g.DrawLine(dashPen2, pointX, chTop, pointX, chBottom);
  499. }
  500. }
  501. else
  502. {
  503. peaks.Add(new Tuple<int, float>(item.HitsPerSecond, pointX));
  504. //g.DrawLine(dashPen2, pointX, chTop, pointX, chBottom);
  505. }
  506. }
  507. }
  508. }
  509. g.DrawLines(scorePen, scorePts.ToArray());
  510. g.DrawLines(comboPen, comboPts.ToArray());
  511. g.DrawLines(accPen, accPts.ToArray());
  512. g.DrawLines(cpsPen, cpsPts.ToArray());
  513. g.DrawLines(calPen, calPts.ToArray());
  514. g.DrawLines(heartPen, heaPts.ToArray());
  515. }
  516. }
  517. }
  518. if (flags.HasFlag(UpdateFlags.Performance) || flags.HasFlag(UpdateFlags.RefreshAll))
  519. {
  520. float x = margin;
  521. float y = chHeight + margin * 3;
  522. //clear region 0,180 Wx180
  523. g.ClearRect(Color.Transparent, 0, y, ViewSize.Width, 150);
  524. //draw text
  525. var sz = g.DrawStringWithOutline($"{CurrentScore:N0}", Font, x, y, Color.Black, Color.White);
  526. x += sz.Width + margin * 2;
  527. sz = g.DrawStringWithRoundedRectPerf("POINT", _smallFont, x, y + margin * 1.5f, Brushes.White, Brushes.Red,
  528. 2.5f);
  529. x += sz.Width + margin * 2;
  530. sz = g.DrawStringWithOutline($"{CurrentCombo}", Font, x, y, Color.Black, Color.White);
  531. x += sz.Width + margin * 2;
  532. sz = g.DrawStringWithRoundedRectPerf("COMBO", _smallFont, x, y + margin * 1.5f, Brushes.White, Brushes.Blue,
  533. 2.5f);
  534. x += sz.Width + margin * 2;
  535. sz = g.DrawStringWithOutline($"{CurrentRank} / {CurrentAcc:N}", Font, x, y, Color.Black, Color.White);
  536. x += sz.Width + margin * 2;
  537. sz = g.DrawStringWithRoundedRectPerf("RANK / ACC", _smallFont, x, y + margin * 1.5f, Brushes.White,
  538. Brushes.Purple, 2.5f);
  539. x += sz.Width + margin * 2;
  540. sz = g.DrawStringWithOutline($"{CurrentHitsPerSecond}", Font, x, y, Color.Black, Color.White);
  541. g.DrawStringWithOutline($"{(MaxHitsPerSecond.HasValue ? $"{MaxHitsPerSecond:N0}" : "-")} max", Font, x, y + Font.Height, Color.Black, Color.White);
  542. x += sz.Width + margin * 2;
  543. sz = g.DrawStringWithRoundedRectPerf("HIT / SEC", _smallFont, x, y + margin * 1.5f, Brushes.Black,
  544. Brushes.Yellow, 2.5f);
  545. x += sz.Width + margin * 2;
  546. sz = g.DrawStringWithOutline($"{CurrentYurStatus.KcalPerMin:N1}", Font, x, y, Color.Black, Color.White);
  547. x += sz.Width + margin * 2;
  548. sz = g.DrawStringWithRoundedRectPerf("KCAL / MIN", _smallFont, x, y + margin * 1.5f, Brushes.Black,
  549. Brushes.Lime, 2.5f);
  550. x += sz.Width + margin * 2;
  551. sz = g.DrawStringWithOutline($"{CurrentYurStatus.HeartRate:N1}", Font, x, y, Color.Black, Color.White);
  552. g.DrawStringWithOutline($"{(MaxHeartRate.HasValue ? $"{MaxHeartRate:N1}" : "-")} max", Font, x, y + Font.Height, Color.Black, Color.White);
  553. x += sz.Width + margin * 2;
  554. sz = g.DrawStringWithRoundedRectPerf("HEART", _smallFont, x, y + margin * 1.5f, Brushes.Black, Brushes.White, 2.5f);
  555. x += sz.Width + margin * 2;
  556. sz = g.DrawStringWithOutline($"{CurrentMissed}", Font, x, y, Color.Black, Color.White);
  557. x += sz.Width + margin * 2;
  558. // ReSharper disable once RedundantAssignment
  559. sz = g.DrawStringWithRoundedRectPerf("MISS", _smallFont, x, y + margin * 1.5f, Brushes.White,
  560. Brushes.DarkSlateGray, 2.5f);
  561. }
  562. if (flags.HasFlag(UpdateFlags.BeatMap) || flags.HasFlag(UpdateFlags.RefreshAll))
  563. {
  564. TopMost = false;
  565. Application.DoEvents();
  566. TopMost = true;
  567. Application.DoEvents();
  568. BringToFront();
  569. Application.DoEvents();
  570. const int coverSize = 130;
  571. g.ClearRect(Color.Transparent, 0, ViewSize.Height - (coverSize + 100), ViewSize.Width, coverSize + 100);
  572. var coverLeft = margin;
  573. var coverTop = ViewSize.Height - coverSize - margin;
  574. g.DrawImage(SongIcon, coverLeft, coverTop, coverSize, coverSize);
  575. g.DrawRectangle(Pens.White, coverLeft, coverTop, coverSize, coverSize);
  576. g.DrawStringWithRoundedRect(Difficulty, Font, coverLeft, coverTop - 100, Brushes.Black, Brushes.White);
  577. {
  578. float x = margin;
  579. var y = coverTop - 50;
  580. var sz = g.DrawStringWithOutline($"{SongBpm}", Font, x, y, Color.Black, Color.White);
  581. x += sz.Width + margin * 2;
  582. sz = g.DrawStringWithRoundedRect("BPM", _smallFont, x, y + margin * 1.5f, Brushes.Black, Brushes.White, 2.5f);
  583. x += sz.Width + margin * 2;
  584. sz = g.DrawStringWithOutline($"{SongNjs}", Font, x, y, Color.Black, Color.White);
  585. x += sz.Width + margin * 2;
  586. // ReSharper disable once RedundantAssignment
  587. sz = g.DrawStringWithRoundedRect("NJS", _smallFont, x, y + margin * 1.5f, Brushes.Black, Brushes.White, 2.5f);
  588. }
  589. {
  590. var bootom = ViewSize.Height - margin;
  591. var left = coverSize + margin * 2;
  592. var fontDeltaH = 1.3f;
  593. var sz = g.MeasureString(SongName, _mediumFont);
  594. g.DrawStringWithOutline(SongName, _mediumFont, left, bootom - sz.Height * fontDeltaH, Color.Black, Color.White);
  595. sz = g.MeasureString(SongSubName, _mediumFont);
  596. g.DrawStringWithOutline(SongSubName, _mediumFont, left, bootom - (sz.Height * fontDeltaH) * 2, Color.Black, Color.White);
  597. sz = g.MeasureString(SongArtist, _mediumFont);
  598. g.DrawStringWithOutline(SongArtist, _mediumFont, left, bootom - (sz.Height * fontDeltaH) * 3, Color.Black, Color.White);
  599. sz = g.MeasureString(BeatMapper, _mediumFont);
  600. g.DrawStringWithOutline(BeatMapper, _mediumFont, left, bootom - (sz.Height * fontDeltaH) * 4, Color.Black, Color.White);
  601. }
  602. }
  603. if (Program.NoPerf == false && (flags.HasFlag(UpdateFlags.CpuAndGpu) || flags.HasFlag(UpdateFlags.RefreshAll)))
  604. {
  605. var width = 800;
  606. var height = 50;
  607. var field = 400;
  608. var left = ViewSize.Width - width;
  609. var top = ViewSize.Height - height;
  610. g.ClearRect(Color.Transparent, left, top, width, height);
  611. g.DrawStringWithOutline($"CPU: {CurrentCpuGhz:N1}GHz {CurrentCpuUsage:N1}%", Font, left, top, Color.Black, Color.White);
  612. g.DrawStringWithOutline($"GPU: {CurrentGpuUsage:N0}%", Font, left + field, top, Color.Black, Color.White);
  613. }
  614. }
  615. }