tools:intervalltrainer-pruefung
Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen der Seite angezeigt.
Beide Seiten, vorherige ÜberarbeitungVorherige ÜberarbeitungNächste Überarbeitung | Vorherige Überarbeitung | ||
tools:intervalltrainer-pruefung [10/05/2025 18:33] – gelöscht - Externe Bearbeitung (Unknown date) 127.0.0.1 | tools:intervalltrainer-pruefung [10/05/2025 18:39] (aktuell) – Eric Weber | ||
---|---|---|---|
Zeile 1: | Zeile 1: | ||
+ | ====== Intervalltrainer | Prüfungsmodus ====== | ||
+ | ;#; | ||
+ | [[tools: | ||
+ | ;#; | ||
+ | |||
+ | |||
+ | |||
+ | < | ||
+ | < | ||
+ | <meta charset=" | ||
+ | <meta name=" | ||
+ | < | ||
+ | < | ||
+ | body { | ||
+ | font-family: | ||
+ | display: flex; | ||
+ | flex-direction: | ||
+ | align-items: | ||
+ | margin: 20px; | ||
+ | background-color: | ||
+ | color: #212529; | ||
+ | } | ||
+ | h1, h2 { | ||
+ | text-align: center; | ||
+ | color: #343a40; | ||
+ | margin-bottom: | ||
+ | } | ||
+ | h1 { | ||
+ | margin-bottom: | ||
+ | } | ||
+ | .settings-section, | ||
+ | margin-bottom: | ||
+ | padding-bottom: | ||
+ | border-bottom: | ||
+ | } | ||
+ | .settings-section: | ||
+ | border-bottom: | ||
+ | margin-bottom: | ||
+ | } | ||
+ | label { | ||
+ | display: block; | ||
+ | margin-bottom: | ||
+ | font-weight: | ||
+ | color: #495057; | ||
+ | } | ||
+ | .interval-checkboxes label, .radio-group label { | ||
+ | display: inline-block; | ||
+ | margin-right: | ||
+ | margin-bottom: | ||
+ | font-weight: | ||
+ | color: #495057; | ||
+ | } | ||
+ | .interval-checkboxes input[type=" | ||
+ | margin-right: | ||
+ | vertical-align: | ||
+ | } | ||
+ | |||
+ | /* --- Button Styling --- */ | ||
+ | button { | ||
+ | padding: 10px 18px; /* Etwas kompakter */ | ||
+ | color: black; | ||
+ | border-radius: | ||
+ | cursor: pointer; | ||
+ | margin-top: 10px; | ||
+ | margin-right: | ||
+ | font-size: 15px; /* Etwas kleiner für mehr Buttons nebeneinander */ | ||
+ | font-weight: | ||
+ | transition: background-color 0.15s ease-in-out, | ||
+ | box-shadow: 0 1px 3px rgba(0, | ||
+ | border-width: | ||
+ | border-style: | ||
+ | } | ||
+ | |||
+ | /* Primäre Buttons (Default) */ | ||
+ | button, button# | ||
+ | background-color: | ||
+ | border-color: | ||
+ | color: black; | ||
+ | } | ||
+ | button: | ||
+ | background-color: | ||
+ | border-color: | ||
+ | box-shadow: 0 2px 6px rgba(0, | ||
+ | } | ||
+ | button: | ||
+ | outline: none; | ||
+ | box-shadow: 0 0 0 0.2rem rgba(38, | ||
+ | } | ||
+ | |||
+ | /* Sekundäre Buttons */ | ||
+ | button.secondary, | ||
+ | | ||
+ | | ||
+ | | ||
+ | } | ||
+ | button.secondary: | ||
+ | background-color: | ||
+ | border-color: | ||
+ | box-shadow: 0 2px 6px rgba(0, | ||
+ | } | ||
+ | button.secondary: | ||
+ | outline: none; | ||
+ | box-shadow: 0 0 0 0.2rem rgba(108, | ||
+ | } | ||
+ | |||
+ | /* Disabled Zustand für alle Buttons */ | ||
+ | button: | ||
+ | background-color: | ||
+ | border-color: | ||
+ | color: #939596 !important; /* Hellerer Text für besseren Kontrast auf Grau */ | ||
+ | cursor: not-allowed !important; | ||
+ | box-shadow: none !important; | ||
+ | opacity: 0.75 !important; | ||
+ | } | ||
+ | |||
+ | /* Antwort-Buttons */ | ||
+ | .answer-button { | ||
+ | margin: 6px; | ||
+ | padding: 10px 15px; /* Angepasst an andere Buttons */ | ||
+ | background-color: | ||
+ | color: #212529; /* Dunkler Text für Lesbarkeit */ | ||
+ | border: 1px solid #ced4da; /* Klarer Rand */ | ||
+ | font-weight: | ||
+ | } | ||
+ | .answer-button: | ||
+ | background-color: | ||
+ | border-color: | ||
+ | color: #0056b3; /* Akzentfarbe bei Hover */ | ||
+ | } | ||
+ | .answer-button: | ||
+ | outline: none; | ||
+ | border-color: | ||
+ | box-shadow: 0 0 0 0.2rem rgba(0, | ||
+ | } | ||
+ | |||
+ | /* Feedback-Buttons (richtig/ | ||
+ | .answer-button.correct { | ||
+ | background-color: | ||
+ | color: green !important; | ||
+ | border-color: | ||
+ | } | ||
+ | .answer-button.incorrect { | ||
+ | background-color: | ||
+ | color: red !important; | ||
+ | border-color: | ||
+ | } | ||
+ | .answer-button.revealed-correct { | ||
+ | background-color: | ||
+ | color: black !important; | ||
+ | border-color: | ||
+ | } | ||
+ | /* --- Ende Button Styling --- */ | ||
+ | |||
+ | |||
+ | #test-area { | ||
+ | text-align: center; | ||
+ | } | ||
+ | .feedback { | ||
+ | margin-top: 20px; | ||
+ | padding: 12px; /* Etwas mehr Padding */ | ||
+ | border-radius: | ||
+ | font-size: 1.15em; /* Etwas größer */ | ||
+ | font-weight: | ||
+ | border-width: | ||
+ | border-style: | ||
+ | } | ||
+ | .feedback.correct { | ||
+ | color: #155724; | ||
+ | background-color: | ||
+ | border-color: | ||
+ | } | ||
+ | .feedback.incorrect { | ||
+ | color: #721c24; | ||
+ | background-color: | ||
+ | border-color: | ||
+ | } | ||
+ | .feedback.revealed { /* Für die Anzeige der richtigen Antwort */ | ||
+ | color: #0c5460; /* Dunkleres Cyan */ | ||
+ | background-color: | ||
+ | border-color: | ||
+ | } | ||
+ | |||
+ | |||
+ | # | ||
+ | margin-top: 25px; | ||
+ | padding: 20px; | ||
+ | background-color: | ||
+ | border-radius: | ||
+ | } | ||
+ | # | ||
+ | margin: 8px 0; | ||
+ | font-size: 1.05em; | ||
+ | color: #495057; | ||
+ | } | ||
+ | .hidden { | ||
+ | display: none; | ||
+ | } | ||
+ | # | ||
+ | font-size: 1.1em; | ||
+ | margin-bottom: | ||
+ | color: #343a40; | ||
+ | } | ||
+ | # | ||
+ | margin-top: 20px; | ||
+ | } | ||
+ | </ | ||
+ | </ | ||
+ | < | ||
+ | |||
+ | <div id=" | ||
+ | < | ||
+ | |||
+ | <div class=" | ||
+ | < | ||
+ | <div class=" | ||
+ | </ | ||
+ | <button id=" | ||
+ | <button id=" | ||
+ | </ | ||
+ | |||
+ | <div class=" | ||
+ | < | ||
+ | <div class=" | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | <div class=" | ||
+ | < | ||
+ | <div class=" | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | <div class=" | ||
+ | < | ||
+ | <div class=" | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | <button id=" | ||
+ | </ | ||
+ | |||
+ | <div id=" | ||
+ | < | ||
+ | <p id=" | ||
+ | <button id=" | ||
+ | <div id=" | ||
+ | </ | ||
+ | <div id=" | ||
+ | <div id=" | ||
+ | <button id=" | ||
+ | <button id=" | ||
+ | <button id=" | ||
+ | </ | ||
+ | |||
+ | <div id=" | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | |||
+ | <div id=" | ||
+ | < | ||
+ | <p>Du hast die Prüfung abgeschlossen!</ | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | <button id=" | ||
+ | </ | ||
+ | |||
+ | |||
+ | < | ||
+ | const intervalDefinitions = [ | ||
+ | { name: " | ||
+ | { name: " | ||
+ | { name: " | ||
+ | { name: " | ||
+ | { name: " | ||
+ | { name: "Reine Quarte", | ||
+ | { name: " | ||
+ | { name: "Reine Quinte", | ||
+ | { name: " | ||
+ | { name: " | ||
+ | { name: " | ||
+ | { name: " | ||
+ | { name: "Reine Oktave", | ||
+ | ]; | ||
+ | |||
+ | const noteNamesDE = [ | ||
+ | " | ||
+ | " | ||
+ | ]; | ||
+ | |||
+ | const baseMidiNotes = { | ||
+ | " | ||
+ | }; | ||
+ | | ||
+ | const midiToGermanNoteName = (midiNote) => { | ||
+ | const noteIndex = midiNote % 12; | ||
+ | const octaveVal = Math.floor(midiNote / 12); | ||
+ | let noteBase = noteNamesDE[noteIndex]; | ||
+ | | ||
+ | if (octaveVal === 4) return noteBase; | ||
+ | if (octaveVal === 5) return noteBase + "'"; | ||
+ | if (octaveVal === 6) return noteBase + "''"; | ||
+ | if (octaveVal === 7) return noteBase + "'''"; | ||
+ | | ||
+ | return noteBase + (octaveVal - 1); | ||
+ | }; | ||
+ | |||
+ | |||
+ | const A4_FREQ = 440; | ||
+ | const A4_MIDI = 69; | ||
+ | |||
+ | const midiToFreq = (midiNote) => { | ||
+ | return A4_FREQ * Math.pow(2, (midiNote - A4_MIDI) / 12); | ||
+ | }; | ||
+ | |||
+ | let audioContext; | ||
+ | let currentTask = {}; | ||
+ | let selectedIntervalsForTest = []; | ||
+ | let playbackSetting = ' | ||
+ | let difficultySetting = ' | ||
+ | let testModeSetting = ' | ||
+ | |||
+ | let currentQuestionNumber = 0; | ||
+ | const totalQuestions = 10; | ||
+ | let correctAnswersCount = 0; | ||
+ | let wrongAnswersCount = 0; | ||
+ | let questionAnswered = false; | ||
+ | |||
+ | |||
+ | // DOM Elements | ||
+ | const settingsContainer = document.getElementById(' | ||
+ | const testArea = document.getElementById(' | ||
+ | const progressArea = document.getElementById(' | ||
+ | const resultsArea = document.getElementById(' | ||
+ | |||
+ | const intervalCheckboxesContainer = document.querySelector(' | ||
+ | const selectAllButton = document.getElementById(' | ||
+ | const deselectAllButton = document.getElementById(' | ||
+ | const startTestButton = document.getElementById(' | ||
+ | |||
+ | const currentQuestionNumberDisplay = document.getElementById(' | ||
+ | const totalQuestionNumberDisplay = document.getElementById(' | ||
+ | const taskDescriptionDisplay = document.getElementById(' | ||
+ | const playIntervalButton = document.getElementById(' | ||
+ | const answerOptionsContainer = document.getElementById(' | ||
+ | const feedbackMessageDisplay = document.getElementById(' | ||
+ | const actionButtonsDiv = document.getElementById(' | ||
+ | const nextQuestionButton = document.getElementById(' | ||
+ | const repeatQuestionButton = document.getElementById(' | ||
+ | const showCorrectAnswerButton = document.getElementById(' | ||
+ | |||
+ | const answeredQuestionsDisplay = document.getElementById(' | ||
+ | const correctAnswersDisplay = document.getElementById(' | ||
+ | const wrongAnswersDisplay = document.getElementById(' | ||
+ | const successRateDisplay = document.getElementById(' | ||
+ | | ||
+ | const finalCorrectDisplay = document.getElementById(' | ||
+ | const finalTotalDisplay = document.getElementById(' | ||
+ | const finalPercentageDisplay = document.getElementById(' | ||
+ | const restartTestButton = document.getElementById(' | ||
+ | |||
+ | |||
+ | function initializeAudio() { | ||
+ | if (!audioContext) { | ||
+ | try { | ||
+ | audioContext = new (window.AudioContext || window.webkitAudioContext)(); | ||
+ | } catch (e) { | ||
+ | alert(' | ||
+ | console.error(' | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | function playNote(frequency, | ||
+ | if (!audioContext) return; | ||
+ | const oscillator = audioContext.createOscillator(); | ||
+ | const gainNode = audioContext.createGain(); | ||
+ | |||
+ | oscillator.type = waveType; | ||
+ | oscillator.frequency.setValueAtTime(frequency, | ||
+ | | ||
+ | gainNode.gain.setValueAtTime(0, | ||
+ | gainNode.gain.linearRampToValueAtTime(0.3, | ||
+ | gainNode.gain.setValueAtTime(0.3, | ||
+ | gainNode.gain.linearRampToValueAtTime(0, | ||
+ | |||
+ | oscillator.connect(gainNode); | ||
+ | gainNode.connect(audioContext.destination); | ||
+ | |||
+ | oscillator.start(audioContext.currentTime + startTime); | ||
+ | oscillator.stop(audioContext.currentTime + startTime + duration + 0.1); | ||
+ | } | ||
+ | |||
+ | function playCurrentInterval() { | ||
+ | initializeAudio(); | ||
+ | if (!audioContext || audioContext.state === ' | ||
+ | audioContext.resume().then(() => { | ||
+ | // console.log(" | ||
+ | actuallyPlayInterval(); | ||
+ | }).catch(err => console.error(" | ||
+ | } else { | ||
+ | actuallyPlayInterval(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | function actuallyPlayInterval() { | ||
+ | if (!currentTask.startNoteMidi || typeof currentTask.endNoteMidi === ' | ||
+ | |||
+ | const freq1 = midiToFreq(currentTask.startNoteMidi); | ||
+ | const freq2 = midiToFreq(currentTask.endNoteMidi); | ||
+ | const noteDuration = 0.7; | ||
+ | |||
+ | playIntervalButton.disabled = true; | ||
+ | let totalPlaybackTime = noteDuration; | ||
+ | |||
+ | if (playbackSetting === ' | ||
+ | playNote(freq1, | ||
+ | playNote(freq2, | ||
+ | totalPlaybackTime = noteDuration * 1.8; | ||
+ | } else if (playbackSetting === ' | ||
+ | playNote(freq1, | ||
+ | playNote(freq2, | ||
+ | totalPlaybackTime = noteDuration * 1.8; | ||
+ | } else { | ||
+ | playNote(freq1, | ||
+ | playNote(freq2, | ||
+ | totalPlaybackTime = noteDuration + 0.3; | ||
+ | } | ||
+ | setTimeout(() => { playIntervalButton.disabled = false; }, totalPlaybackTime * 1000 + 200); | ||
+ | } | ||
+ | |||
+ | |||
+ | function populateIntervalCheckboxes() { | ||
+ | intervalDefinitions.forEach(interval => { | ||
+ | const div = document.createElement(' | ||
+ | const label = document.createElement(' | ||
+ | const checkbox = document.createElement(' | ||
+ | checkbox.type = ' | ||
+ | checkbox.value = interval.id; | ||
+ | checkbox.id = `interval-${interval.id}`; | ||
+ | checkbox.dataset.semitones = interval.semitones; | ||
+ | checkbox.checked = true; | ||
+ | label.appendChild(checkbox); | ||
+ | label.appendChild(document.createTextNode(` ${interval.name}`)); | ||
+ | div.appendChild(label); | ||
+ | intervalCheckboxesContainer.appendChild(div); | ||
+ | }); | ||
+ | } | ||
+ | |||
+ | function toggleAllIntervals(select) { | ||
+ | intervalCheckboxesContainer.querySelectorAll(' | ||
+ | cb.checked = select; | ||
+ | }); | ||
+ | } | ||
+ | |||
+ | function collectSettings() { | ||
+ | selectedIntervalsForTest = Array.from(intervalCheckboxesContainer.querySelectorAll(' | ||
+ | .map(cb => intervalDefinitions.find(i => i.id === cb.value)) | ||
+ | .sort((a, b) => a.semitones - b.semitones); | ||
+ | |||
+ | playbackSetting = document.querySelector(' | ||
+ | difficultySetting = document.querySelector(' | ||
+ | testModeSetting = document.querySelector(' | ||
+ | |||
+ | if (selectedIntervalsForTest.length === 0) { | ||
+ | alert(" | ||
+ | return false; | ||
+ | } | ||
+ | return true; | ||
+ | } | ||
+ | | ||
+ | function generateRandomMidiNote(minMidi, | ||
+ | return Math.floor(Math.random() * (maxMidi - minMidi + 1)) + minMidi; | ||
+ | } | ||
+ | |||
+ | |||
+ | function generateQuestion() { | ||
+ | questionAnswered = false; | ||
+ | currentQuestionNumber++; | ||
+ | updateProgress(); | ||
+ | |||
+ | const randomIntervalDef = selectedIntervalsForTest[Math.floor(Math.random() * selectedIntervalsForTest.length)]; | ||
+ | currentTask.interval = randomIntervalDef; | ||
+ | let startNoteMidi; | ||
+ | |||
+ | const c3Midi = baseMidiNotes[" | ||
+ | const c4Midi = baseMidiNotes[" | ||
+ | const c5Midi = baseMidiNotes[" | ||
+ | const c6Midi = baseMidiNotes[" | ||
+ | |||
+ | |||
+ | if (difficultySetting === ' | ||
+ | if (playbackSetting === ' | ||
+ | startNoteMidi = c5Midi; | ||
+ | } else { | ||
+ | startNoteMidi = c4Midi; | ||
+ | } | ||
+ | } else if (difficultySetting === ' | ||
+ | const ascStarts = [c3Midi, c4Midi, c5Midi]; | ||
+ | const descStarts = [c4Midi, c5Midi, c6Midi]; | ||
+ | if (playbackSetting === ' | ||
+ | startNoteMidi = descStarts[Math.floor(Math.random() * descStarts.length)]; | ||
+ | } else { | ||
+ | startNoteMidi = ascStarts[Math.floor(Math.random() * ascStarts.length)]; | ||
+ | } | ||
+ | } else { | ||
+ | const minPlayableMidi = c3Midi; | ||
+ | const maxPlayableMidi = c6Midi + 11; | ||
+ | |||
+ | if (playbackSetting === ' | ||
+ | const minStart = minPlayableMidi + randomIntervalDef.semitones; | ||
+ | startNoteMidi = generateRandomMidiNote(Math.max(minStart, | ||
+ | } else { | ||
+ | const maxStart = maxPlayableMidi - randomIntervalDef.semitones; | ||
+ | startNoteMidi = generateRandomMidiNote(c3Midi, | ||
+ | } | ||
+ | } | ||
+ | |||
+ | currentTask.startNoteMidi = startNoteMidi; | ||
+ | if (playbackSetting === ' | ||
+ | currentTask.endNoteMidi = startNoteMidi - randomIntervalDef.semitones; | ||
+ | } else { | ||
+ | currentTask.endNoteMidi = startNoteMidi + randomIntervalDef.semitones; | ||
+ | } | ||
+ | | ||
+ | if (playbackSetting === ' | ||
+ | if (currentTask.startNoteMidi > currentTask.endNoteMidi) { | ||
+ | [currentTask.startNoteMidi, | ||
+ | } | ||
+ | } | ||
+ | |||
+ | currentTask.startNoteName = midiToGermanNoteName(currentTask.startNoteMidi); | ||
+ | currentTask.endNoteName = midiToGermanNoteName(currentTask.endNoteMidi); | ||
+ | | ||
+ | displayQuestion(); | ||
+ | } | ||
+ | |||
+ | |||
+ | function displayQuestion() { | ||
+ | currentQuestionNumberDisplay.textContent = currentQuestionNumber; | ||
+ | totalQuestionNumberDisplay.textContent = totalQuestions; | ||
+ | answerOptionsContainer.innerHTML = ''; | ||
+ | feedbackMessageDisplay.textContent = ''; | ||
+ | feedbackMessageDisplay.className = ' | ||
+ | actionButtonsDiv.classList.add(' | ||
+ | nextQuestionButton.classList.add(' | ||
+ | repeatQuestionButton.classList.add(' | ||
+ | showCorrectAnswerButton.classList.add(' | ||
+ | playIntervalButton.disabled = false; | ||
+ | |||
+ | |||
+ | if (testModeSetting === ' | ||
+ | taskDescriptionDisplay.textContent = " | ||
+ | const options = [...selectedIntervalsForTest]; | ||
+ | |||
+ | options.forEach(interval => { | ||
+ | const button = document.createElement(' | ||
+ | button.textContent = interval.name; | ||
+ | button.classList.add(' | ||
+ | button.dataset.intervalId = interval.id; | ||
+ | button.onclick = () => checkAnswer(interval.id); | ||
+ | answerOptionsContainer.appendChild(button); | ||
+ | }); | ||
+ | currentTask.correctAnswer = currentTask.interval.id; | ||
+ | |||
+ | } else { | ||
+ | let displayStartNote = currentTask.startNoteName; | ||
+ | if (playbackSetting === ' | ||
+ | | ||
+ | } else if (playbackSetting === ' | ||
+ | // displayStartNote remains currentTask.startNoteName (which is the higher note) | ||
+ | } | ||
+ | |||
+ | taskDescriptionDisplay.textContent = `Der Startton ist ${displayStartNote}. Welches ist der Zielton?`; | ||
+ | currentTask.correctAnswer = currentTask.endNoteName; | ||
+ | |||
+ | let noteOptionsMidi = [currentTask.endNoteMidi]; | ||
+ | const numTotalOptions = Math.min(5, Math.max(3, selectedIntervalsForTest.length > 0 ? selectedIntervalsForTest.length : 3)); | ||
+ | |||
+ | let attempts = 0; | ||
+ | const maxAttempts = 30; | ||
+ | const deviationRange = 4; | ||
+ | |||
+ | while (noteOptionsMidi.length < numTotalOptions && attempts < maxAttempts) { | ||
+ | const randomDeviation = Math.floor(Math.random() * (2 * deviationRange + 1)) - deviationRange; | ||
+ | if (randomDeviation === 0 && noteOptionsMidi.includes(currentTask.endNoteMidi + randomDeviation) ) { | ||
+ | attempts++; | ||
+ | continue; | ||
+ | } | ||
+ | const potentialWrongNoteMidi = currentTask.endNoteMidi + randomDeviation; | ||
+ | | ||
+ | if (potentialWrongNoteMidi >= baseMidiNotes[" | ||
+ | noteOptionsMidi.push(potentialWrongNoteMidi); | ||
+ | } | ||
+ | attempts++; | ||
+ | } | ||
+ | | ||
+ | if (noteOptionsMidi.length < numTotalOptions) { | ||
+ | let otherIntervalChoices = [...selectedIntervalsForTest].filter(i => i.id !== currentTask.interval.id); | ||
+ | otherIntervalChoices.sort(() => 0.5 - Math.random()); | ||
+ | |||
+ | for (let i = 0; i < otherIntervalChoices.length && noteOptionsMidi.length < numTotalOptions; | ||
+ | let otherTargetMidi; | ||
+ | const referenceStartMidi = (playbackSetting === ' | ||
+ | |||
+ | if (playbackSetting === ' | ||
+ | otherTargetMidi = referenceStartMidi - otherIntervalChoices[i].semitones; | ||
+ | } else { | ||
+ | otherTargetMidi = referenceStartMidi + otherIntervalChoices[i].semitones; | ||
+ | } | ||
+ | if (!noteOptionsMidi.includes(otherTargetMidi) && otherTargetMidi >= baseMidiNotes[" | ||
+ | noteOptionsMidi.push(otherTargetMidi); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | | ||
+ | if (!noteOptionsMidi.includes(currentTask.endNoteMidi)) { | ||
+ | if (noteOptionsMidi.length >= numTotalOptions) noteOptionsMidi.pop(); | ||
+ | noteOptionsMidi.push(currentTask.endNoteMidi); | ||
+ | } | ||
+ | |||
+ | noteOptionsMidi.sort((a, | ||
+ | |||
+ | noteOptionsMidi.forEach(noteMidi => { | ||
+ | const button = document.createElement(' | ||
+ | const noteName = midiToGermanNoteName(noteMidi); | ||
+ | button.textContent = noteName; | ||
+ | button.classList.add(' | ||
+ | button.dataset.noteName = noteName; | ||
+ | button.onclick = () => checkAnswer(noteName); | ||
+ | answerOptionsContainer.appendChild(button); | ||
+ | }); | ||
+ | } | ||
+ | if (currentQuestionNumber > 0) setTimeout(playCurrentInterval, | ||
+ | } | ||
+ | |||
+ | function checkAnswer(selectedValue) { | ||
+ | if (questionAnswered) return; | ||
+ | questionAnswered = true; | ||
+ | |||
+ | const isCorrect = (selectedValue === currentTask.correctAnswer); | ||
+ | const clickedButton = Array.from(answerOptionsContainer.querySelectorAll(' | ||
+ | return testModeSetting === ' | ||
+ | }); | ||
+ | |||
+ | playIntervalButton.disabled = true; | ||
+ | |||
+ | if (isCorrect) { | ||
+ | correctAnswersCount++; | ||
+ | feedbackMessageDisplay.textContent = " | ||
+ | feedbackMessageDisplay.className = ' | ||
+ | if(clickedButton) clickedButton.classList.add(' | ||
+ | | ||
+ | actionButtonsDiv.classList.remove(' | ||
+ | nextQuestionButton.classList.remove(' | ||
+ | repeatQuestionButton.classList.add(' | ||
+ | showCorrectAnswerButton.classList.add(' | ||
+ | |||
+ | } else { | ||
+ | wrongAnswersCount++; | ||
+ | feedbackMessageDisplay.textContent = " | ||
+ | feedbackMessageDisplay.className = ' | ||
+ | if(clickedButton) clickedButton.classList.add(' | ||
+ | |||
+ | actionButtonsDiv.classList.remove(' | ||
+ | repeatQuestionButton.classList.remove(' | ||
+ | showCorrectAnswerButton.classList.remove(' | ||
+ | nextQuestionButton.classList.add(' | ||
+ | } | ||
+ | updateProgress(); | ||
+ | disableAnswerButtons(); | ||
+ | |||
+ | if (currentQuestionNumber >= totalQuestions) { | ||
+ | | ||
+ | if (!isCorrect) { | ||
+ | showCorrectAnswerButton.onclick = () => { | ||
+ | revealCorrectAnswer(true); | ||
+ | }; | ||
+ | } else { // if correct and last question | ||
+ | actionButtonsDiv.classList.remove(' | ||
+ | nextQuestionButton.classList.remove(' | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | | ||
+ | function disableAnswerButtons() { | ||
+ | answerOptionsContainer.querySelectorAll(' | ||
+ | } | ||
+ | function enableAnswerButtons() { | ||
+ | answerOptionsContainer.querySelectorAll(' | ||
+ | } | ||
+ | |||
+ | |||
+ | function handleRepeatQuestion() { | ||
+ | feedbackMessageDisplay.textContent = ''; | ||
+ | feedbackMessageDisplay.className = ' | ||
+ | enableAnswerButtons(); | ||
+ | questionAnswered = false; | ||
+ | playIntervalButton.disabled = false; | ||
+ | actionButtonsDiv.classList.add(' | ||
+ | |||
+ | answerOptionsContainer.querySelectorAll(' | ||
+ | btn.classList.remove(' | ||
+ | }); | ||
+ | playCurrentInterval(); | ||
+ | } | ||
+ | |||
+ | function revealCorrectAnswer(isEndOfTestPath = false) { | ||
+ | const correctButton = Array.from(answerOptionsContainer.querySelectorAll(' | ||
+ | | ||
+ | }); | ||
+ | if (correctButton && !correctButton.classList.contains(' | ||
+ | correctButton.classList.add(' | ||
+ | } | ||
+ | | ||
+ | const correctAnswerText = testModeSetting === ' | ||
+ | feedbackMessageDisplay.textContent = `Die richtige Antwort war: ${correctAnswerText}.`; | ||
+ | feedbackMessageDisplay.className = ' | ||
+ | | ||
+ | actionButtonsDiv.classList.remove(' | ||
+ | nextQuestionButton.classList.remove(' | ||
+ | if (currentQuestionNumber >= totalQuestions || isEndOfTestPath) { | ||
+ | nextQuestionButton.textContent = " | ||
+ | } | ||
+ | repeatQuestionButton.classList.add(' | ||
+ | showCorrectAnswerButton.classList.add(' | ||
+ | } | ||
+ | |||
+ | |||
+ | function handleNextQuestion() { | ||
+ | if (currentQuestionNumber >= totalQuestions) { | ||
+ | showResults(); | ||
+ | } else { | ||
+ | playIntervalButton.disabled = false; | ||
+ | enableAnswerButtons(); | ||
+ | answerOptionsContainer.querySelectorAll(' | ||
+ | btn.classList.remove(' | ||
+ | }); | ||
+ | generateQuestion(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | |||
+ | function updateProgress() { | ||
+ | const answeredTotal = correctAnswersCount + wrongAnswersCount; | ||
+ | answeredQuestionsDisplay.textContent = answeredTotal; | ||
+ | correctAnswersDisplay.textContent = correctAnswersCount; | ||
+ | wrongAnswersDisplay.textContent = wrongAnswersCount; | ||
+ | const rate = answeredTotal > 0 ? Math.round((correctAnswersCount / answeredTotal) * 100) : 0; | ||
+ | successRateDisplay.textContent = rate; | ||
+ | } | ||
+ | | ||
+ | function showResults() { | ||
+ | testArea.classList.add(' | ||
+ | progressArea.classList.add(' | ||
+ | resultsArea.classList.remove(' | ||
+ | |||
+ | finalCorrectDisplay.textContent = correctAnswersCount; | ||
+ | finalTotalDisplay.textContent = totalQuestions; | ||
+ | const percentage = totalQuestions > 0 ? Math.round((correctAnswersCount / totalQuestions) * 100) : 0; | ||
+ | finalPercentageDisplay.textContent = percentage; | ||
+ | } | ||
+ | |||
+ | function resetAndRestart() { | ||
+ | currentQuestionNumber = 0; | ||
+ | correctAnswersCount = 0; | ||
+ | wrongAnswersCount = 0; | ||
+ | questionAnswered = false; | ||
+ | currentTask = {}; | ||
+ | |||
+ | resultsArea.classList.add(' | ||
+ | settingsContainer.classList.remove(' | ||
+ | progressArea.classList.add(' | ||
+ | testArea.classList.add(' | ||
+ | |||
+ | nextQuestionButton.textContent = " | ||
+ | showCorrectAnswerButton.onclick = () => revealCorrectAnswer(false); | ||
+ | | ||
+ | answeredQuestionsDisplay.textContent = 0; | ||
+ | correctAnswersDisplay.textContent = 0; | ||
+ | wrongAnswersDisplay.textContent = 0; | ||
+ | successRateDisplay.textContent = 0; | ||
+ | } | ||
+ | |||
+ | |||
+ | document.addEventListener(' | ||
+ | populateIntervalCheckboxes(); | ||
+ | totalQuestionNumberDisplay.textContent = totalQuestions; | ||
+ | |||
+ | selectAllButton.addEventListener(' | ||
+ | deselectAllButton.addEventListener(' | ||
+ | | ||
+ | startTestButton.addEventListener(' | ||
+ | initializeAudio(); | ||
+ | if (audioContext && audioContext.state === ' | ||
+ | audioContext.resume().catch(err => console.error(" | ||
+ | } | ||
+ | |||
+ | if (collectSettings()) { | ||
+ | settingsContainer.classList.add(' | ||
+ | testArea.classList.remove(' | ||
+ | progressArea.classList.remove(' | ||
+ | resultsArea.classList.add(' | ||
+ | currentQuestionNumber = 0; | ||
+ | correctAnswersCount = 0; | ||
+ | wrongAnswersCount = 0; | ||
+ | | ||
+ | answeredQuestionsDisplay.textContent = 0; | ||
+ | correctAnswersDisplay.textContent = 0; | ||
+ | wrongAnswersDisplay.textContent = 0; | ||
+ | successRateDisplay.textContent = 0; | ||
+ | generateQuestion(); | ||
+ | } | ||
+ | }); | ||
+ | |||
+ | playIntervalButton.addEventListener(' | ||
+ | nextQuestionButton.addEventListener(' | ||
+ | repeatQuestionButton.addEventListener(' | ||
+ | showCorrectAnswerButton.addEventListener(' | ||
+ | restartTestButton.addEventListener(' | ||
+ | }); | ||
+ | |||
+ | </ | ||
+ | </ | ||
+ | </ |