tools:tonhoehentrainer
Dies ist eine alte Version des Dokuments!
<!DOCTYPE html> <html lang=„de“> <head>
<meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Gehörtraining: Tonhöhenvergleich</title> <style> .ear-training-app { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; background-color: #f5f5f5; } .ear-training-app h1, .ear-training-app h2 { color: #333; text-align: center; } .ear-training-app .container { background-color: white; border-radius: 10px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); padding: 20px; margin-bottom: 20px; } .ear-training-app .settings { display: flex; flex-direction: column; gap: 15px; } .ear-training-app .difficulty-options { display: flex; gap: 10px; justify-content: center; margin: 10px 0; } .ear-training-app .difficulty-btn { padding: 10px 15px; border: none; border-radius: 5px; cursor: pointer; font-weight: bold; transition: background-color 0.3s; } .ear-training-app .difficulty-btn.active { background-color: #4CAF50; color: white; } .ear-training-app .start-btn { background-color: #4CAF50; color: white; padding: 12px 20px; border: none; border-radius: 5px; cursor: pointer; font-size: 16px; font-weight: bold; margin: 10px auto; display: block; } .ear-training-app .play-tones-btn { background-color: #2196F3; color: white; padding: 12px 20px; border: none; border-radius: 5px; cursor: pointer; font-size: 16px; font-weight: bold; margin: 10px auto; display: block; } .ear-training-app .answer-btns { display: flex; justify-content: center; gap: 20px; margin: 20px 0; } .ear-training-app .answer-btn { padding: 15px 30px; border: none; border-radius: 5px; cursor: pointer; font-size: 16px; font-weight: bold; transition: background-color 0.3s; } .ear-training-app .higher-btn { background-color: #FFEB3B; } .ear-training-app .lower-btn { background-color: #FFEB3B; } .ear-training-app .correct { background-color: #4CAF50 !important; color: white; } .ear-training-app .incorrect { background-color: #F44336 !important; color: white; } .ear-training-app .progress { display: flex; justify-content: space-between; margin-top: 20px; } .ear-training-app .progress-dots { display: flex; gap: 10px; margin-bottom: 10px; } .ear-training-app .dot { width: 20px; height: 20px; border-radius: 50%; background-color: #ddd; } .ear-training-app .dot.correct { background-color: #4CAF50; } .ear-training-app .dot.incorrect { background-color: #F44336; } .ear-training-app .dot.current { border: 2px solid #2196F3; } .ear-training-app .stats { text-align: center; font-weight: bold; margin-top: 10px; } .ear-training-app .hidden { display: none; } .ear-training-app .cancel-btn { background-color: #9E9E9E; color: white; padding: 10px 15px; border: none; border-radius: 5px; cursor: pointer; font-weight: bold; margin-top: 10px; display: block; margin: 10px auto; } .ear-training-app .result-message { text-align: center; font-size: 18px; margin-top: 20px; font-weight: bold; } .ear-training-app .audio-status { text-align: center; margin: 10px 0; padding: 10px; background-color: #FFF9C4; border-radius: 5px; } .ear-training-app .difficulty-description { font-size: 14px; text-align: center; margin-top: 5px; color: #666; } .ear-training-app .next-btn { background-color: #FF9800; color: white; padding: 10px 15px; border: none; border-radius: 5px; cursor: pointer; font-weight: bold; margin: 10px 5px; display: inline-block; } .ear-training-app #retry-next { text-align: center; } </style>
</head> <body>
<div class="ear-training-app"> <h1>Gehörtraining: Tonhöhenvergleich</h1> <div class="container settings"> <h2>Einstellungen</h2> <div> <p>Wähle den Schwierigkeitsgrad:</p> <div class="difficulty-options"> <button class="difficulty-btn active" data-difficulty="easy">Einfach</button> <button class="difficulty-btn" data-difficulty="medium">Fortgeschritten</button> <button class="difficulty-btn" data-difficulty="hard">Profi</button> </div> <div class="difficulty-description" id="difficulty-description"> Einfach: Große Terz (maximal 4 Halbtöne Unterschied) </div> </div> <div id="audio-permission" class="audio-status"> Bitte klicke hier um Audio zu aktivieren </div> <button id="start-test" class="start-btn">Prüfung starten</button> </div>
<div id="test-area" class="container hidden"> <button id="play-tones" class="play-tones-btn">Töne abspielen</button> <div class="answer-btns"> <button id="higher-btn" class="answer-btn higher-btn">Höher</button> <button id="lower-btn" class="answer-btn lower-btn">Tiefer</button> </div> <div id="retry-next" class="hidden"> <button id="retry-btn" class="play-tones-btn">Nochmal hören</button> <button id="next-btn" class="next-btn">Nächste Frage</button> </div> <div class="progress"> <div class="progress-dots" id="progress-dots"></div> </div> <div class="stats" id="stats"> Frage 1 von 10 | Richtig: 0% | Falsch: 0% </div> <button id="cancel-test" class="cancel-btn">Prüfung abbrechen</button> </div> <div id="final-result" class="container hidden"> <h2>Prüfungsergebnis</h2> <div class="result-message" id="result-message"></div> <button id="restart-btn" class="start-btn">Neue Prüfung starten</button> </div> </div>
<script> // Warten, bis die Seite vollständig geladen ist window.addEventListener('DOMContentLoaded', () => { // DOM-Elemente const startTestBtn = document.getElementById('start-test'); const testArea = document.getElementById('test-area'); const playTonesBtn = document.getElementById('play-tones'); const higherBtn = document.getElementById('higher-btn'); const lowerBtn = document.getElementById('lower-btn'); const progressDots = document.getElementById('progress-dots'); const statsDiv = document.getElementById('stats'); const finalResult = document.getElementById('final-result'); const resultMessage = document.getElementById('result-message'); const restartBtn = document.getElementById('restart-btn'); const audioPermission = document.getElementById('audio-permission'); const cancelTestBtn = document.getElementById('cancel-test'); const difficultyDescription = document.getElementById('difficulty-description'); const retryNextDiv = document.getElementById('retry-next'); const retryBtn = document.getElementById('retry-btn'); const nextBtn = document.getElementById('next-btn'); // Audio Context let audioContext = null; let audioInitialized = false; // Schwierigkeitsgrad-Buttons const difficultyBtns = document.querySelectorAll('.ear-training-app .difficulty-btn'); // Variablen für das Spiel let difficulty = 'easy'; // Standardschwierigkeit: einfach let currentQuestion = 0; let correctAnswers = 0; let totalQuestions = 10; let questions = []; let currentFirstNote = null; let currentSecondNote = null; let isSecondNoteHigher = false; // Tonhöhen (C4 bis A5) const notes = ['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']; // Frequenzen der Noten const noteFrequencies = { 'C4': 261.63, 'C#4': 277.18, 'D4': 293.66, 'D#4': 311.13, 'E4': 329.63, 'F4': 349.23, 'F#4': 369.99, 'G4': 392.00, 'G#4': 415.30, 'A4': 440.00, 'A#4': 466.16, 'B4': 493.88, 'C5': 523.25, 'C#5': 554.37, 'D5': 587.33, 'D#5': 622.25, 'E5': 659.25, 'F5': 698.46, 'F#5': 739.99, 'G5': 783.99, 'G#5': 830.61, 'A5': 880.00 }; // Schwierigkeitsgradbeschreibungen const difficultyDescriptions = { 'easy': 'Einfach: Große Terz (maximal 4 Halbtöne Unterschied)', 'medium': 'Fortgeschritten: Quinte (maximal 7 Halbtöne Unterschied)', 'hard': 'Profi: Oktave (maximal 12 Halbtöne Unterschied)' }; // Audio initialisieren function initializeAudio() { if (!audioInitialized) { try { audioContext = new (window.AudioContext || window.webkitAudioContext)(); audioInitialized = true; audioPermission.textContent = "Audio aktiviert ✓"; audioPermission.style.backgroundColor = "#DCEDC8"; } catch (e) { console.error("Web Audio API wird nicht unterstützt.", e); audioPermission.textContent = "Audio nicht verfügbar. Bitte verwende einen modernen Browser."; audioPermission.style.backgroundColor = "#FFCDD2"; } } } // Audio-Erlaubnis anfordern audioPermission.addEventListener('click', initializeAudio); // Ton abspielen mit Web Audio API function playNote(note, delay = 0) { if (!audioContext) { alert("Bitte aktiviere zuerst Audio durch Klicken auf den Audio-Status-Bereich."); return; } const frequency = noteFrequencies[note]; if (!frequency) return; // Zeit für den Start des Tons const startTime = audioContext.currentTime + delay; // Oszillator erstellen const oscillator = audioContext.createOscillator(); oscillator.type = 'sine'; // Sinuswelle für einen weicheren Ton oscillator.frequency.setValueAtTime(frequency, startTime); // Envelope für einen Piano-ähnlichen Klang const gainNode = audioContext.createGain(); gainNode.gain.setValueAtTime(0, startTime); gainNode.gain.linearRampToValueAtTime(0.7, startTime + 0.01); gainNode.gain.linearRampToValueAtTime(0.6, startTime + 0.1); gainNode.gain.exponentialRampToValueAtTime(0.001, startTime + 1.5); // Verbindungen herstellen oscillator.connect(gainNode); gainNode.connect(audioContext.destination); // Ton starten und stoppen oscillator.start(startTime); oscillator.stop(startTime + 1.5); } // Zwei Töne hintereinander abspielen function playTones() { if (!currentFirstNote || !currentSecondNote) return; // Audio initialisieren, falls noch nicht geschehen if (!audioInitialized) { initializeAudio(); } // Alle Schaltflächen deaktivieren während des Abspielens higherBtn.disabled = true; lowerBtn.disabled = true; // Ersten Ton abspielen playNote(currentFirstNote, 0); // Zweiten Ton nach 1,5 Sekunden abspielen playNote(currentSecondNote, 1.5); // Nach 3 Sekunden Schaltflächen wieder aktivieren setTimeout(() => { higherBtn.disabled = false; lowerBtn.disabled = false; }, 3000); } // Zufälligen Ton zwischen C4 und A5 generieren function getRandomNote() { const randomIndex = Math.floor(Math.random() * notes.length); return notes[randomIndex]; } // Zweiten Ton basierend auf dem Schwierigkeitsgrad generieren function generateSecondNote(firstNote) { const firstNoteIndex = notes.indexOf(firstNote); let maxDistance; // Maximale Entfernung basierend auf dem Schwierigkeitsgrad switch (difficulty) { case 'easy': // Große Terz (4 Halbtöne) maxDistance = 4; break; case 'medium': // Quinte (7 Halbtöne) maxDistance = 7; break; case 'hard': // Oktave (12 Halbtöne) maxDistance = 12; break; default: maxDistance = 4; } // Zufällige Richtung (höher oder tiefer) const direction = Math.random() < 0.5 ? -1 : 1; // Zufällige Distanz innerhalb der maximalen Entfernung let distance = Math.floor(Math.random() * maxDistance) + 1; distance *= direction; // Sicherstellen, dass der neue Index innerhalb des gültigen Bereichs liegt let newIndex = firstNoteIndex + distance; if (newIndex < 0) newIndex = 0; if (newIndex >= notes.length) newIndex = notes.length - 1; // Vermeiden, dass beide Töne gleich sind if (newIndex === firstNoteIndex) { newIndex += (direction > 0) ? 1 : -1; if (newIndex < 0) newIndex = 1; if (newIndex >= notes.length) newIndex = notes.length - 2; } // Ist der zweite Ton höher? isSecondNoteHigher = newIndex > firstNoteIndex; return notes[newIndex]; } // Neue Frage generieren function generateQuestion() { const firstNote = getRandomNote(); const secondNote = generateSecondNote(firstNote); return { firstNote, secondNote, isSecondNoteHigher }; } // Fortschrittsanzeige aktualisieren function updateProgress() { // Fortschrittspunkte aktualisieren progressDots.innerHTML = ''; for (let i = 0; i < totalQuestions; i++) { const dot = document.createElement('div'); dot.classList.add('dot'); // Status des Punktes basierend auf dem Spielfortschritt if (i < currentQuestion) { if (questions[i].correct) { dot.classList.add('correct'); } else { dot.classList.add('incorrect'); } } else if (i === currentQuestion) { dot.classList.add('current'); } progressDots.appendChild(dot); } // Statistiken aktualisieren const answered = Math.min(currentQuestion, totalQuestions); let correctPercentage = 0; let incorrectPercentage = 0; if (answered > 0) { correctPercentage = Math.round((correctAnswers / answered) * 100); incorrectPercentage = 100 - correctPercentage; } statsDiv.textContent = `Frage ${currentQuestion + 1} von ${totalQuestions} | ` + `Richtig: ${correctPercentage}% | ` + `Falsch: ${incorrectPercentage}%`; } // Prüfung starten function startTest() { // Audio initialisieren, falls noch nicht geschehen if (!audioInitialized) { initializeAudio(); } // Fragen generieren questions = []; for (let i = 0; i < totalQuestions; i++) { questions.push(generateQuestion()); } // Variablen zurücksetzen currentQuestion = 0; correctAnswers = 0; // Aktuelle Frage setzen currentFirstNote = questions[0].firstNote; currentSecondNote = questions[0].secondNote; isSecondNoteHigher = questions[0].isSecondNoteHigher; // UI aktualisieren testArea.classList.remove('hidden'); document.querySelector('.ear-training-app .settings').classList.add('hidden'); finalResult.classList.add('hidden'); retryNextDiv.classList.add('hidden'); higherBtn.classList.remove('correct', 'incorrect'); lowerBtn.classList.remove('correct', 'incorrect'); // Fortschritt aktualisieren updateProgress(); // Töne automatisch abspielen setTimeout(playTones, 500); } // Prüfung abbrechen function cancelTest() { testArea.classList.add('hidden'); document.querySelector('.ear-training-app .settings').classList.remove('hidden'); } // Zur nächsten Frage gehen function nextQuestion() { currentQuestion++; // Prüfen, ob alle Fragen beantwortet wurden if (currentQuestion >= totalQuestions) { showFinalResult(); return; } // Aktuelle Frage setzen currentFirstNote = questions[currentQuestion].firstNote; currentSecondNote = questions[currentQuestion].secondNote; isSecondNoteHigher = questions[currentQuestion].isSecondNoteHigher; // UI zurücksetzen retryNextDiv.classList.add('hidden'); higherBtn.classList.remove('correct', 'incorrect'); lowerBtn.classList.remove('correct', 'incorrect'); // Fortschritt aktualisieren updateProgress(); // Töne automatisch abspielen setTimeout(playTones, 500); } // Endergebnis anzeigen function showFinalResult() { testArea.classList.add('hidden'); finalResult.classList.remove('hidden'); const percentage = Math.round((correctAnswers / totalQuestions) * 100); let message; if (percentage >= 90) { message = `Ausgezeichnet! Du hast ${percentage}% der Fragen richtig beantwortet!`; } else if (percentage >= 70) { message = `Gut gemacht! Du hast ${percentage}% der Fragen richtig beantwortet.`; } else if (percentage >= 50) { message = `Nicht schlecht! Du hast ${percentage}% der Fragen richtig beantwortet.`; } else { message = `Du hast ${percentage}% der Fragen richtig beantwortet. Übe weiter!`; } resultMessage.textContent = message; } // Schwierigkeitsgradbeschreibung aktualisieren function updateDifficultyDescription(selectedDifficulty) { difficultyDescription.textContent = difficultyDescriptions[selectedDifficulty]; } // Event-Listener // Schwierigkeitsgrad wählen difficultyBtns.forEach(btn => { btn.addEventListener('click', () => { // Aktiven Button zurücksetzen difficultyBtns.forEach(b => b.classList.remove('active')); // Neuen Button aktivieren btn.classList.add('active'); // Schwierigkeitsgrad setzen difficulty = btn.dataset.difficulty; // Beschreibung aktualisieren updateDifficultyDescription(difficulty); }); }); // Prüfung starten startTestBtn.addEventListener('click', startTest); // Töne abspielen playTonesBtn.addEventListener('click', playTones); // Prüfung abbrechen cancelTestBtn.addEventListener('click', cancelTest); // Antwort: Höher higherBtn.addEventListener('click', () => { // Antwort überprüfen const isCorrect = isSecondNoteHigher; // Antwort speichern questions[currentQuestion].correct = isCorrect; if (isCorrect) { correctAnswers++; higherBtn.classList.add('correct'); // Bei richtiger Antwort nach 1 Sekunde automatisch zur nächsten Frage setTimeout(nextQuestion, 1000); } else { higherBtn.classList.add('incorrect'); // Bei falscher Antwort Optionen anzeigen retryNextDiv.classList.remove('hidden'); } // Fortschritt aktualisieren updateProgress(); }); // Antwort: Tiefer lowerBtn.addEventListener('click', () => { // Antwort überprüfen const isCorrect = !isSecondNoteHigher; // Antwort speichern questions[currentQuestion].correct = isCorrect; if (isCorrect) { correctAnswers++; lowerBtn.classList.add('correct'); // Bei richtiger Antwort nach 1 Sekunde automatisch zur nächsten Frage setTimeout(nextQuestion, 1000); } else { lowerBtn.classList.add('incorrect'); // Bei falscher Antwort Optionen anzeigen retryNextDiv.classList.remove('hidden'); } // Fortschritt aktualisieren updateProgress(); }); // "Nochmal hören" Button retryBtn.addEventListener('click', () => { // Die gleichen Töne erneut abspielen playTones(); }); // "Nächste Frage" Button nextBtn.addEventListener('click', () => { // Zur nächsten Frage gehen nextQuestion(); }); // Prüfung neu starten restartBtn.addEventListener('click', () => { finalResult.classList.add('hidden'); document.querySelector('.ear-training-app .settings').classList.remove('hidden'); }); }); </script>
</body> </html>
tools/tonhoehentrainer.1747669918.txt.gz · Zuletzt geändert: von Eric Weber