tuner (buggy?)

This commit is contained in:
2025-12-21 22:26:19 +01:00
parent 3b162e4ba9
commit 4029f62f43
5 changed files with 213 additions and 16 deletions

View File

@@ -1,4 +1,4 @@
import { Settings, Guitar, Music } from 'lucide-react';
import { Settings, Guitar, Music, Gauge } from 'lucide-react';
import { TUNINGS, INSTRUMENT_TUNINGS } from '../music/Tunings';
import { INSTRUMENT_DEFINITIONS } from '../music/InstrumentConfigs';
@@ -20,6 +20,7 @@ export interface AppSettings {
tuningId: string;
keySignature: string;
instrument: string;
showTuningMeter: boolean;
rhythm: RhythmSettings;
}
@@ -227,7 +228,19 @@ export const Controls: React.FC<ControlsProps> = ({ settings, onUpdateSettings }
</div>
)}
</div>
<div className="control-group" style={{ borderTop: '1px solid rgba(255,255,255,0.1)', paddingTop: '12px', marginTop: '12px', width: '100%', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<label className="control-label" style={{ marginBottom: 0 }}>
<Gauge size={18} />
<span>Tuning Meter</span>
</label>
<button
className={`switch-button ${settings.showTuningMeter ? 'active' : ''}`}
onClick={() => onUpdateSettings({ ...settings, showTuningMeter: !settings.showTuningMeter })}
title={settings.showTuningMeter ? "Hide Tuner" : "Show Tuner"}
>
<div className="switch-thumb" />
</button>
</div>
</div>

View File

@@ -0,0 +1,118 @@
.tuning-meter-container {
display: flex;
flex-direction: column;
align-items: center;
width: 260px;
background: rgba(0, 0, 0, 0.4);
padding: 12px;
border-radius: 12px;
backdrop-filter: blur(4px);
border: 1px solid rgba(255, 255, 255, 0.1);
margin-top: 10px;
}
.meter-scale {
position: relative;
width: 100%;
height: 24px;
background: rgba(255, 255, 255, 0.1);
border-radius: 12px;
margin-bottom: 8px;
overflow: hidden;
}
.tick {
position: absolute;
top: 4px;
font-size: 10px;
color: rgba(255, 255, 255, 0.5);
transform: translateX(-50%);
}
.tick-left {
left: 10%;
}
.tick-center {
left: 50%;
}
.tick-right {
left: 90%;
}
.center-line {
position: absolute;
left: 50%;
top: 0;
bottom: 0;
width: 2px;
background: rgba(255, 255, 255, 0.3);
transform: translateX(-50%);
z-index: 1;
}
.meter-needle {
position: absolute;
top: 0;
bottom: 0;
width: 4px;
background: white;
transform: translateX(-50%);
transition: left 0.1s ease-out;
z-index: 2;
border-radius: 2px;
box-shadow: 0 0 8px rgba(255, 255, 255, 0.5);
}
.meter-needle.perfect {
background-color: #4ade80;
box-shadow: 0 0 10px #4ade80;
}
/* Tailwind Green-400 */
.meter-needle.good {
background-color: #86efac;
}
/* Tailwind Green-300 */
.meter-needle.okay {
background-color: #facc15;
}
/* Tailwind Yellow-400 */
.meter-needle.poor {
background-color: #f87171;
}
/* Tailwind Red-400 */
.tuning-readout {
display: flex;
align-items: baseline;
gap: 8px;
font-family: monospace;
}
.tuning-readout .note-name {
font-size: 1.25rem;
font-weight: bold;
color: white;
}
.tuning-readout .cents-value {
font-size: 0.9rem;
color: rgba(255, 255, 255, 0.7);
}
.tuning-readout .cents-value.perfect {
color: #4ade80;
}
.tuning-readout .cents-value.okay {
color: #facc15;
}
.tuning-readout .cents-value.poor {
color: #f87171;
}

View File

@@ -0,0 +1,58 @@
import React from 'react';
import './TuningMeter.css';
interface TuningMeterProps {
cents: number; // Deviation in cents from nearest note (-50 to +50 usually)
noteName: string; // The detected note name (e.g. "A4")
}
export const TuningMeter: React.FC<TuningMeterProps> = ({ cents, noteName }) => {
// Clamp cents for display (-50 to 50)
const clampedCents = Math.max(-50, Math.min(50, cents));
// Map -50..50 to 0..100% for position
// -50 cents -> 0%
// 0 cents -> 50%
// 50 cents -> 100%
const positionPercent = ((clampedCents + 50) / 100) * 100;
// Color determination
let statusClass = 'neutral';
if (Math.abs(cents) < 5) {
statusClass = 'perfect'; // Green
} else if (Math.abs(cents) < 15) {
statusClass = 'good'; // Green-ish
} else if (Math.abs(cents) < 30) {
statusClass = 'okay'; // Yellow
} else {
statusClass = 'poor'; // Red
}
return (
<div className="tuning-meter-container">
<div className="meter-scale">
<div className="tick tick-left">-50</div>
<div className="tick tick-center">0</div>
<div className="tick tick-right">+50</div>
{/* Center Marker Line */}
<div className="center-line"></div>
{/* The Needle/Indicator */}
<div
className={`meter-needle ${statusClass}`}
style={{ left: `${positionPercent}%` }}
>
<div className="needle-head"></div>
</div>
</div>
<div className="tuning-readout">
<span className="note-name">{noteName}</span>
<span className={`cents-value ${statusClass}`}>
{cents > 0 ? '+' : ''}{Math.round(cents)} ct
</span>
</div>
</div>
);
};