Tap function fix #7

This commit is contained in:
Artem Anufrij
2026-10-09 10:37:04 +02:00
parent 3f7047ab07
commit 7fed1cd4dd
2 changed files with 93 additions and 17 deletions
+63 -17
View File
@@ -13,6 +13,12 @@ const STORAGE_KEY_PRESETS = "ClickPresets"
const STORAGE_KEY_VIEW = "ClickView"
const STORAGE_KEY_SORT = "ClickSort"
// TAP
const TAP_RESET_MS = 2000; // nach dieser Pause beginnt eine neue Messung
const TAP_MAX_SAMPLES = 8; // so viele letzte Taps werden gemittelt
let tapTimes = [];
// Reaktiver Zustand (für die UI)
const bpm = ref(120);
const isRunning = ref(false);
@@ -81,20 +87,7 @@ async function start() {
if (isRunning.value) return;
clampBpm();
try {
// AudioContext erst durch User-Interaktion starten
if (!audioContext) {
audioContext = new AudioContext();
}
if (!clickBuffer) {
await loadClickSound();
}
if (audioContext.state === "suspended") {
await audioContext.resume();
}
} catch (error) {
console.error("Metronom konnte nicht gestartet werden:", error);
if (!initAudioInterface()) {
return;
}
@@ -112,6 +105,25 @@ function stop() {
}
}
function initAudioInterface() {
try {
// AudioContext erst durch User-Interaktion starten
if (!audioContext) {
audioContext = new AudioContext();
}
if (!clickBuffer) {
loadClickSound();
}
if (audioContext.state === "suspended") {
audioContext.resume();
}
} catch (error) {
console.error("Metronom konnte nicht gestartet werden:", error);
return false;
}
return true;
}
onBeforeUnmount(() => {
stop();
if (audioContext) {
@@ -122,6 +134,9 @@ onBeforeUnmount(() => {
//PRESETS
function loadPresets() {
initAudioInterface();
try {
const raw = localStorage.getItem(STORAGE_KEY_PRESETS);
return raw ? JSON.parse(raw) : [];
@@ -204,6 +219,35 @@ function setSort(val) {
localStorage.setItem(STORAGE_KEY_SORT, val);
}
// TAP
function tap() {
stop();
selectedPreset.value = null;
playClick(0);
const now = performance.now();
// Lange Pause seit dem letzten Tap: neu beginnen
if (tapTimes.length && now - tapTimes[tapTimes.length - 1] > TAP_RESET_MS) {
tapTimes = [];
}
tapTimes.push(now);
if (tapTimes.length > TAP_MAX_SAMPLES) {
tapTimes.shift();
}
// Mindestens zwei Taps nötig, um einen Abstand zu haben
if (tapTimes.length < 2) return;
// Durchschnittlicher Abstand zwischen den Taps in ms
const intervals = tapTimes.slice(1).map((t, i) => t - tapTimes[i]);
const avg = intervals.reduce((sum, v) => sum + v, 0) / intervals.length;
bpm.value = Math.round(60000 / avg);
clampBpm();
}
// EXPORT / IMPORT
function exportPresets() {
const json = JSON.stringify(presets.value, null, 2);
@@ -318,7 +362,7 @@ watch(
</div>
</main>
<footer>
<div class="left">
<div class="grow left">
<button @click="$refs.fileInput.click()">
<font-awesome-icon :icon="faUpload" size="xl" />
</button>
@@ -326,8 +370,10 @@ watch(
<font-awesome-icon :icon="faDownload" size="xl" />
</button>
</div>
<div class="grow"></div>
<div class="right">
<div class="grow center">
<button id="tapButton" @click="tap">TAP</button>
</div>
<div class="grow right">
<button v-if="view == 'grid'" @click="setView('list')">
<font-awesome-icon :icon="faList" size="xl" />
</button>
+30
View File
@@ -223,6 +223,36 @@ input[type="number"] {
flex-grow: 1;
}
.left,
.center,
.right {
flex: 1 1 0;
min-width: 0;
display: flex;
gap: 0.5rem;
}
.left {
justify-content: flex-start;
}
.center {
justify-content: center;
}
.right {
justify-content: flex-end;
}
#tapButton {
border-radius: 32px;
height: 64px;
width: 64px;
margin: -8px;
background: var(--secondary);
box-shadow: 0 0px 4px var(--shadow);
}
/* DIALOG */
#dialog {
position: fixed;