Benutzer-Werkzeuge

Webseiten-Werkzeuge


tools:skalentrainer

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen der Seite angezeigt.

Link zu der Vergleichsansicht

Beide Seiten, vorherige ÜberarbeitungVorherige Überarbeitung
Nächste Überarbeitung
Vorherige Überarbeitung
tools:skalentrainer [15/05/2025 08:59] – ↷ Seitename wurde von tools:skalentrainer-pruefung auf tools:skalentrainer geändert Eric Webertools:skalentrainer [05/04/2026 15:55] (aktuell) Eric Weber
Zeile 1: Zeile 1:
-====== Skalentrainer ====== 
- 
- 
 <html> <html>
 <head> <head>
Zeile 8: Zeile 5:
     <title>Musikalische Skalen Trainer</title>     <title>Musikalische Skalen Trainer</title>
     <style>     <style>
-        body {+        .skalen-trainer-app {
             font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;             font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
             line-height: 1.6;             line-height: 1.6;
Zeile 16: Zeile 13:
             padding: 20px;             padding: 20px;
             background-color: #f5f5f5;             background-color: #f5f5f5;
 +            box-sizing: border-box;
         }         }
-        h1, h2 {+ 
 +        .skalen-trainer-app * { 
 +            box-sizing: border-box; 
 +        } 
 + 
 +        .skalen-trainer-app h1, 
 +        .skalen-trainer-app h2 {
             color: #2c3e50;             color: #2c3e50;
             text-align: center;             text-align: center;
         }         }
-        .container {+ 
 +        .skalen-trainer-app .container {
             background-color: white;             background-color: white;
             border-radius: 8px;             border-radius: 8px;
Zeile 28: Zeile 33:
             margin-bottom: 20px;             margin-bottom: 20px;
         }         }
-        .flex-row {+ 
 +        .skalen-trainer-app .flex-row {
             display: flex;             display: flex;
             flex-wrap: wrap;             flex-wrap: wrap;
Zeile 35: Zeile 41:
             margin-bottom: 15px;             margin-bottom: 15px;
         }         }
-        .scales-container {+ 
 +        .skalen-trainer-app .scales-container {
             display: flex;             display: flex;
             flex-wrap: wrap;             flex-wrap: wrap;
Zeile 41: Zeile 48:
             margin-bottom: 15px;             margin-bottom: 15px;
         }         }
-        .checkbox-container {+ 
 +        .skalen-trainer-app .checkbox-container {
             display: flex;             display: flex;
             align-items: center;             align-items: center;
Zeile 50: Zeile 58:
             transition: background-color 0.2s;             transition: background-color 0.2s;
         }         }
-        .checkbox-container:hover {+ 
 +        .skalen-trainer-app .checkbox-container:hover {
             background-color: #d5dbdb;             background-color: #d5dbdb;
         }         }
-        .checkbox-container input {+ 
 +        .skalen-trainer-app .checkbox-container input {
             margin-right: 8px;             margin-right: 8px;
         }         }
-        label {+ 
 +        .skalen-trainer-app label {
             margin-right: 10px;             margin-right: 10px;
             font-weight: 500;             font-weight: 500;
         }         }
-        button {+ 
 +        .skalen-trainer-app button {
             background-color: #2c3e50;             background-color: #2c3e50;
             color: black;             color: black;
Zeile 71: Zeile 83:
             font-weight: 600;             font-weight: 600;
             box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);             box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
 +            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
         }         }
-        button:hover {+ 
 +        .skalen-trainer-app button:hover {
             background-color: #34495e;             background-color: #34495e;
             transform: translateY(-2px);             transform: translateY(-2px);
             box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);             box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
         }         }
-        button:active {+ 
 +        .skalen-trainer-app button:active {
             transform: translateY(0);             transform: translateY(0);
             box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);             box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
         }         }
-        button:disabled {+ 
 +        .skalen-trainer-app button:disabled {
             background-color: #95a5a6;             background-color: #95a5a6;
             border-color: #7f8c8d;             border-color: #7f8c8d;
Zeile 87: Zeile 103:
             box-shadow: none;             box-shadow: none;
         }         }
-        .select-all-btn {+ 
 +        .skalen-trainer-app .select-all-btn {
             margin-left: auto;             margin-left: auto;
         }         }
-        .mode-selector {+ 
 +        .skalen-trainer-app .mode-selector {
             display: flex;             display: flex;
             justify-content: center;             justify-content: center;
Zeile 96: Zeile 114:
             margin-bottom: 20px;             margin-bottom: 20px;
         }         }
-        .mode-btn {+ 
 +        .skalen-trainer-app .mode-btn {
             padding: 10px 20px;             padding: 10px 20px;
             background-color: #ecf0f1;             background-color: #ecf0f1;
Zeile 103: Zeile 122:
             font-weight: 600;             font-weight: 600;
         }         }
-        .mode-btn.active {+ 
 +        .skalen-trainer-app .mode-btn.active {
             background-color: #2c3e50;             background-color: #2c3e50;
             color: black;             color: black;
             border-color: #1a2530;             border-color: #1a2530;
         }         }
-        #practiceSection, #quizSection {+ 
 +        .skalen-trainer-app #practiceSection, 
 +        .skalen-trainer-app #quizSection {
             margin-top: 30px;             margin-top: 30px;
         }         }
-        #quizSection {+ 
 +        .skalen-trainer-app #quizSection {
             display: none;             display: none;
         }         }
-        .quiz-options {+ 
 +        .skalen-trainer-app .practice-scales-container { 
 +            display: grid; 
 +            grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); 
 +            gap: 15px; 
 +            margin: 20px 0; 
 +        } 
 + 
 +        .skalen-trainer-app .practice-scale-btn { 
 +            background-color: #ecf0f1; 
 +            color: #2c3e50; 
 +            border: 2px solid #bdc3c7; 
 +            padding: 15px; 
 +            border-radius: 8px; 
 +            cursor: pointer; 
 +            font-size: 16px; 
 +            font-weight: 600; 
 +            text-align: center; 
 +            transition: all 0.2s; 
 +        } 
 + 
 +        .skalen-trainer-app .practice-scale-btn:hover { 
 +            background-color: #d5dbdb; 
 +            transform: translateY(-2px); 
 +            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); 
 +        } 
 + 
 +        .skalen-trainer-app .practice-scale-btn:active { 
 +            transform: translateY(0); 
 +            box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); 
 +        } 
 + 
 +        .skalen-trainer-app .practice-scale-btn.playing { 
 +            background-color: #3498db; 
 +            color: black; 
 +            border-color: #2980b9; 
 +        } 
 + 
 +        .skalen-trainer-app .quiz-options {
             display: grid;             display: grid;
             grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));             grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
Zeile 120: Zeile 181:
             margin: 20px 0;             margin: 20px 0;
         }         }
-        .quiz-option {+ 
 +        .skalen-trainer-app .quiz-option {
             background-color: #ecf0f1;             background-color: #ecf0f1;
             padding: 12px;             padding: 12px;
Zeile 127: Zeile 189:
             cursor: pointer;             cursor: pointer;
             transition: all 0.2s;             transition: all 0.2s;
 +            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
         }         }
-        .quiz-option:hover {+ 
 +        .skalen-trainer-app .quiz-option:hover {
             background-color: #d5dbdb;             background-color: #d5dbdb;
         }         }
-        .correct {+ 
 +        .skalen-trainer-app .correct {
             background-color: #27ae60 !important;             background-color: #27ae60 !important;
-            color: black;+            color: white;
         }         }
-        .incorrect {+ 
 +        .skalen-trainer-app .incorrect {
             background-color: #c0392b !important;             background-color: #c0392b !important;
-            color: black;+            color: white;
         }         }
-        .progress-container {+ 
 +        .skalen-trainer-app .progress-container {
             margin-top: 30px;             margin-top: 30px;
             text-align: center;             text-align: center;
         }         }
-        .progress-bar {+ 
 +        .skalen-trainer-app .progress-bar {
             height: 10px;             height: 10px;
             background-color: #ecf0f1;             background-color: #ecf0f1;
Zeile 150: Zeile 218:
             overflow: hidden;             overflow: hidden;
         }         }
-        .progress-fill {+ 
 +        .skalen-trainer-app .progress-fill {
             height: 100%;             height: 100%;
             background-color: #3498db;             background-color: #3498db;
Zeile 156: Zeile 225:
             transition: width 0.3s;             transition: width 0.3s;
         }         }
-        .progress-markers {+ 
 +        .skalen-trainer-app .progress-markers {
             display: flex;             display: flex;
             justify-content: space-between;             justify-content: space-between;
             margin-top: 5px;             margin-top: 5px;
         }         }
-        .progress-marker {+ 
 +        .skalen-trainer-app .progress-marker {
             width: 20px;             width: 20px;
             height: 20px;             height: 20px;
Zeile 171: Zeile 242:
             font-size: 12px;             font-size: 12px;
         }         }
-        .progress-marker.completed {+ 
 +        .skalen-trainer-app .progress-marker.completed {
             background-color: #3498db;             background-color: #3498db;
-            color: black;+            color: white;
         }         }
-        .progress-marker.correct {+ 
 +        .skalen-trainer-app .progress-marker.correct {
             background-color: #27ae60;             background-color: #27ae60;
-            color: black;+            color: white;
         }         }
-        .progress-marker.incorrect {+ 
 +        .skalen-trainer-app .progress-marker.incorrect {
             background-color: #c0392b;             background-color: #c0392b;
-            color: black;+            color: white;
         }         }
-        #keySelector {+ 
 +        .skalen-trainer-app #keySelector {
             display: none;             display: none;
             margin-top: 15px;             margin-top: 15px;
         }         }
-        .separator {+ 
 +        .skalen-trainer-app .separator {
             margin: 0 10px;             margin: 0 10px;
             color: #7f8c8d;             color: #7f8c8d;
         }         }
-        .control-section {+ 
 +        .skalen-trainer-app .control-section {
             display: flex;             display: flex;
             gap: 10px;             gap: 10px;
             margin: 20px 0;             margin: 20px 0;
         }         }
-        #playAgainBtn, #showAnswerBtn, #nextQuestionBtn {+ 
 +        .skalen-trainer-app #playAgainBtn, 
 +        .skalen-trainer-app #showAnswerBtn, 
 +        .skalen-trainer-app #nextQuestionBtn {
             display: none;             display: none;
         }         }
-        #results {+ 
 +        .skalen-trainer-app #results {
             font-weight: bold;             font-weight: bold;
             font-size: 18px;             font-size: 18px;
             margin-top: 20px;             margin-top: 20px;
             text-align: center;             text-align: center;
 +        }
 +
 +        @media (max-width: 768px) {
 +            .skalen-trainer-app .mode-selector {
 +                flex-direction: column;
 +                align-items: center;
 +            }
         }         }
     </style>     </style>
 </head> </head>
 <body> <body>
-     +    <div class="skalen-trainer-app"> 
-    <div class="container"> +        <h1>Skalen-Trainer</h1> 
-        <h2>Einstellungen</h2> + 
-         +        <div class="container"> 
-        <!-- Mode selection --> +            <h2>Einstellungen</h2> 
-        <div class="mode-selector"> + 
-            <button class="mode-btn active" data-mode="easy">Einfach<br>(Ohne Vorzeichen)</button> +            <div class="mode-selector"> 
-            <button class="mode-btn" data-mode="intermediate">Fortgeschritten<br>(Bis 3 Vorzeichen)</button> +                <button class="mode-btn active" data-mode="easy">Einfach<br>(Ohne Vorzeichen)</button> 
-            <button class="mode-btn" data-mode="advanced">Profi<br>(Alle Vorzeichen)</button+                <button class="mode-btn" data-mode="intermediate">Fortgeschritten<br>(Bis 3 Vorzeichen)</button> 
-        </div> +                <button class="mode-btn" data-mode="advanced">Profi<br>(Alle Vorzeichen)</button>
-         +
-        <!-- Scales selection --> +
-        <div class="flex-row"> +
-            <label>Skalen:</label> +
-            <button id="toggleAllBtn" class="select-all-btn">Alle auswählen</button> +
-        </div> +
-         +
-        <div class="scales-container"> +
-            <div class="checkbox-container"> +
-                <input type="checkbox" id="scale-ionian"> +
-                <label for="scale-ionian">Dur (Ionisch)</label>+
             </div>             </div>
-            <div class="checkbox-container"> + 
-                <input type="checkbox" id="scale-aeolian"+            <div class="flex-row"> 
-                <label for="scale-aeolian">Moll natürlich (Aeolisch)</label>+                <label>Skalen:</label
 +                <button id="toggleAllBtn" class="select-all-btn">Alle auswählen</button>
             </div>             </div>
-            <div class="checkbox-container"> + 
-                <input type="checkbox" id="scale-harmonic-minor"> +            <div class="scales-container"> 
-                <label for="scale-harmonic-minor">Moll harmonisch</label>+                <div class="checkbox-container"
 +                    <input type="checkbox" id="scale-ionian"> 
 +                    <label for="scale-ionian">Dur (Ionisch)</label
 +                </div> 
 +                <div class="checkbox-container"> 
 +                    <input type="checkbox" id="scale-aeolian"> 
 +                    <label for="scale-aeolian">Moll natürlich (Aeolisch)</label> 
 +                </div> 
 +                <div class="checkbox-container"> 
 +                    <input type="checkbox" id="scale-harmonic-minor"> 
 +                    <label for="scale-harmonic-minor">Moll harmonisch</label
 +                </div> 
 +                <div class="checkbox-container"> 
 +                    <input type="checkbox" id="scale-melodic-minor"> 
 +                    <label for="scale-melodic-minor">Moll melodisch</label> 
 +                </div> 
 +                <div class="checkbox-container"> 
 +                    <input type="checkbox" id="scale-dorian"> 
 +                    <label for="scale-dorian">Dorisch</label> 
 +                </div> 
 +                <div class="checkbox-container"> 
 +                    <input type="checkbox" id="scale-lydian"> 
 +                    <label for="scale-lydian">Lydisch</label> 
 +                </div> 
 +                <div class="checkbox-container"> 
 +                    <input type="checkbox" id="scale-mixolydian"> 
 +                    <label for="scale-mixolydian">Mixolydisch</label> 
 +                </div> 
 +                <div class="checkbox-container"> 
 +                    <input type="checkbox" id="scale-phrygian"> 
 +                    <label for="scale-phrygian">Phrygisch</label> 
 +                </div> 
 +                <div class="checkbox-container"> 
 +                    <input type="checkbox" id="scale-locrian"> 
 +                    <label for="scale-locrian">Lokrisch</label> 
 +                </div>
             </div>             </div>
-            <div class="checkbox-container"+        </div> 
-                <input type="checkboxid="scale-melodic-minor"> + 
-                <label for="scale-melodic-minor">Moll melodisch</label>+        <div id="practiceSectionclass="container"> 
 +            <h2>Übungsmodus</h2> 
 +            <p>Klicke auf eine Skala, um sie abzuspielen:</p> 
 + 
 +            <div class="practice-scales-container" id="practiceScalesContainer">
             </div>             </div>
-            <div class="checkbox-container"+ 
-                <input type="checkbox" id="scale-dorian"> +            <div class="flex-rowstyle="justify-content: center; margin-top: 20px;"> 
-                <label for="scale-dorian">Dorisch</label>+                <button id="startQuizBtn">Prüfung starten</button>
             </div>             </div>
-            <div class="checkbox-container"> +        </div
-                <input type="checkbox" id="scale-lydian"> + 
-                <label for="scale-lydian">Lydisch</label>+        <div id="quizSection" class="container"> 
 +            <h2>Prüfungsmodus</h2> 
 +            <p id="quizInstruction">Höre die Skala und wähle die richtige Antwort:</p> 
 + 
 +            <button id="playScaleBtn">Skala abspielen</button> 
 + 
 +            <div class="quiz-options" id="quizOptions">
             </div>             </div>
-            <div class="checkbox-container"> + 
-                <input type="checkbox" id="scale-mixolydian"> +            <div class="control-section"> 
-                <label for="scale-mixolydian">Mixolydisch</label>+                <button id="playAgainBtn">Noch einmal abspielen</button> 
 +                <button id="showAnswerBtn">Lösung zeigen</button
 +                <button id="nextQuestionBtn">Nächste Frage</button>
             </div>             </div>
-            <div class="checkbox-container"> + 
-                <input type="checkboxid="scale-phrygian"> +            <div class="progress-container"> 
-                <label for="scale-phrygian">Phrygisch</label+                <div id="progressText">Frage 0 von 10</div> 
-            </div> +                <div class="progress-bar"> 
-            <div class="checkbox-container"> +                    <div class="progress-fill" id="progressFill"></div
-                <input type="checkbox" id="scale-locrian"> +                </div> 
-                <label for="scale-locrian">Lokrisch</label>+                <div class="progress-markers" id="progressMarkers"> 
 +                </div> 
 +                <div id="results"></div>
             </div>             </div>
         </div>         </div>
-    </div> 
  
-    <div id="practiceSection" class="container"> +        <div style="text-align: right; margin-top: 16px; font-size: 0.75rem; color: #95a5a6;"> 
-        <h2>Übungsmodus</h2> +            Made with <a href="https://claude.aitarget="_blankstyle="color#3498db; text-decoration: none;">claude.ai</a
-        <div class="flex-row"> +            by <a href="https://www.ericweber.detarget="_blankstyle="color: #3498db; text-decoration: none;">Eric Weber</a>
-            <button id="startPracticeBtn">Skalen abspielen</button> +
-            <span class="separator">|</span> +
-            <button id="startQuizBtn">Prüfung starten</button> +
-        </div> +
-    </div> +
- +
-    <div id="quizSectionclass="container"> +
-        <h2>Prüfungsmodus</h2> +
-        <p id="quizInstruction">Höre die Skala und wähle die richtige Antwort:</p> +
-         +
-        <button id="playScaleBtn">Skala abspielen</button> +
-         +
-        <div class="quiz-options" id="quizOptions"> +
-            <!-- Options will be generated here --> +
-        </div> +
-         +
-        <div class="control-section"+
-            <button id="playAgainBtn">Noch einmal abspielen</button> +
-            <button id="showAnswerBtn">Lösung zeigen</button> +
-            <button id="nextQuestionBtn">Nächste Frage</button> +
-        </div> +
-         +
-        <div class="progress-container"+
-            <div id="progressText">Frage 0 von 10</div> +
-            <div class="progress-bar"> +
-                <div class="progress-fill" id="progressFill"></div> +
-            </div> +
-            <div class="progress-markers" id="progressMarkers"> +
-                <!-- Markers will be generated here --> +
-            </div> +
-            <div id="results"></div>+
         </div>         </div>
     </div>     </div>
  
     <script>     <script>
-        // Audio context and main variables 
         let audioContext;         let audioContext;
         let currentMode = "easy";         let currentMode = "easy";
-        let allScalesSelected = true;+        let allScalesSelected = false;
         let quizInProgress = false;         let quizInProgress = false;
         let currentQuizQuestion = 0;         let currentQuizQuestion = 0;
Zeile 314: Zeile 408:
         let currentScale;         let currentScale;
         let questionAnswered = false;         let questionAnswered = false;
 +        let currentlyPlayingScale = null;
  
-        // Function to stop all audio 
         function stopAllAudio() {         function stopAllAudio() {
-            // If there's an active quiz or practice, stop it 
             if (quizInProgress) {             if (quizInProgress) {
                 resetQuiz();                 resetQuiz();
             }             }
-             +            resetPracticeScaleButtons();
-            // If audio context exists, cancel all scheduled sounds+
             if (audioContext) {             if (audioContext) {
-                // Create a new audio context to stop all sounds 
                 audioContext.close().then(() => {                 audioContext.close().then(() => {
                     audioContext = null;                     audioContext = null;
Zeile 330: Zeile 421:
             }             }
         }         }
-         +
-        // Reset quiz state+
         function resetQuiz() {         function resetQuiz() {
             if (quizInProgress) {             if (quizInProgress) {
Zeile 347: Zeile 437:
         }         }
  
-        // Initialize the application+        function resetPracticeScaleButtons() { 
 +            document.querySelectorAll(".practice-scale-btn").forEach(btn => { 
 +                btn.classList.remove("playing"); 
 +            }); 
 +            currentlyPlayingScale = null; 
 +        } 
         document.addEventListener("DOMContentLoaded", function() {         document.addEventListener("DOMContentLoaded", function() {
-            // Set up event listeners 
             document.getElementById("toggleAllBtn").addEventListener("click", toggleAllScales);             document.getElementById("toggleAllBtn").addEventListener("click", toggleAllScales);
-            document.getElementById("startPracticeBtn").addEventListener("click", startPractice); 
             document.getElementById("startQuizBtn").addEventListener("click", startQuiz);             document.getElementById("startQuizBtn").addEventListener("click", startQuiz);
             document.getElementById("playScaleBtn").addEventListener("click", () => playCurrentQuizScale());             document.getElementById("playScaleBtn").addEventListener("click", () => playCurrentQuizScale());
Zeile 358: Zeile 452:
             document.getElementById("nextQuestionBtn").addEventListener("click", nextQuestion);             document.getElementById("nextQuestionBtn").addEventListener("click", nextQuestion);
  
-            // Mode buttons 
             document.querySelectorAll(".mode-btn").forEach(btn => {             document.querySelectorAll(".mode-btn").forEach(btn => {
                 btn.addEventListener("click", function() {                 btn.addEventListener("click", function() {
                     setMode(this.dataset.mode);                     setMode(this.dataset.mode);
                     stopAllAudio();                     stopAllAudio();
 +                    updatePracticeScales();
                 });                 });
             });             });
  
-            // Add event listeners to stop audio when settings change 
             document.querySelectorAll(".scales-container input[type='checkbox']").forEach(checkbox => {             document.querySelectorAll(".scales-container input[type='checkbox']").forEach(checkbox => {
-                checkbox.addEventListener("change", stopAllAudio);+                checkbox.addEventListener("change", function() { 
 +                    stopAllAudio(); 
 +                    updatePracticeScales(); 
 +                });
             });             });
  
-            // Initialize progress markers 
             initializeProgressMarkers();             initializeProgressMarkers();
 +            updatePracticeScales();
         });         });
  
-        // Toggle all scales selection 
         function toggleAllScales() {         function toggleAllScales() {
             const scaleCheckboxes = document.querySelectorAll(".scales-container input[type='checkbox']");             const scaleCheckboxes = document.querySelectorAll(".scales-container input[type='checkbox']");
             allScalesSelected = !allScalesSelected;             allScalesSelected = !allScalesSelected;
-             
             scaleCheckboxes.forEach(checkbox => {             scaleCheckboxes.forEach(checkbox => {
                 checkbox.checked = allScalesSelected;                 checkbox.checked = allScalesSelected;
             });             });
-             
             document.getElementById("toggleAllBtn").textContent = allScalesSelected ? "Alle abwählen" : "Alle auswählen";             document.getElementById("toggleAllBtn").textContent = allScalesSelected ? "Alle abwählen" : "Alle auswählen";
 +            updatePracticeScales();
         }         }
  
-        // Set the difficulty mode 
         function setMode(mode) {         function setMode(mode) {
             currentMode = mode;             currentMode = mode;
-             
-            // Update UI 
             document.querySelectorAll(".mode-btn").forEach(btn => {             document.querySelectorAll(".mode-btn").forEach(btn => {
                 btn.classList.remove("active");                 btn.classList.remove("active");
Zeile 398: Zeile 489:
                 }                 }
             });             });
-            +        }
  
 +        function updatePracticeScales() {
 +            const container = document.getElementById("practiceScalesContainer");
 +            container.innerHTML = "";
 +            const selectedScales = getSelectedScales();
 +            if (selectedScales.length === 0) {
 +                container.innerHTML = "<p style='text-align: center; color: #666; grid-column: 1 / -1;'>Bitte wähle mindestens eine Skala aus.</p>";
 +                return;
 +            }
 +            selectedScales.forEach(scale => {
 +                const button = document.createElement("button");
 +                button.className = "practice-scale-btn";
 +                button.textContent = getScaleName(scale);
 +                button.dataset.scale = scale;
 +                button.addEventListener("click", function() {
 +                    playPracticeScale(scale, this);
 +                });
 +                container.appendChild(button);
 +            });
 +        }
 +
 +        function playPracticeScale(scale, buttonElement) {
 +            resetPracticeScaleButtons();
 +            buttonElement.classList.add("playing");
 +            currentlyPlayingScale = scale;
 +            const startNote = getScaleStartNote(scale);
 +            playScale(scale, startNote, () => {
 +                if (currentlyPlayingScale === scale) {
 +                    buttonElement.classList.remove("playing");
 +                    currentlyPlayingScale = null;
 +                }
 +            });
         }         }
  
-        // Check if at least one scale is selected 
         function isAnyScaleSelected() {         function isAnyScaleSelected() {
             const scaleCheckboxes = document.querySelectorAll(".scales-container input[type='checkbox']");             const scaleCheckboxes = document.querySelectorAll(".scales-container input[type='checkbox']");
Zeile 408: Zeile 529:
         }         }
  
-        // Get selected scales 
         function getSelectedScales() {         function getSelectedScales() {
             const scales = [];             const scales = [];
-             
             if (document.getElementById("scale-ionian").checked) scales.push("ionian");             if (document.getElementById("scale-ionian").checked) scales.push("ionian");
             if (document.getElementById("scale-aeolian").checked) scales.push("aeolian");             if (document.getElementById("scale-aeolian").checked) scales.push("aeolian");
Zeile 421: Zeile 540:
             if (document.getElementById("scale-phrygian").checked) scales.push("phrygian");             if (document.getElementById("scale-phrygian").checked) scales.push("phrygian");
             if (document.getElementById("scale-locrian").checked) scales.push("locrian");             if (document.getElementById("scale-locrian").checked) scales.push("locrian");
-             
             return scales;             return scales;
         }         }
  
-        // Initialize the audio context (must be triggered by user interaction) 
         function initAudioContext() {         function initAudioContext() {
             if (!audioContext) {             if (!audioContext) {
Zeile 433: Zeile 550:
         }         }
  
-        // Scale definitions (intervals in semitones) 
         const scaleDefinitions = {         const scaleDefinitions = {
             "ionian": {             "ionian": {
Zeile 449: Zeile 565:
             "melodic-minor": {             "melodic-minor": {
                 ascending: [0, 2, 3, 5, 7, 9, 11, 12],                 ascending: [0, 2, 3, 5, 7, 9, 11, 12],
-                // Melodic minor descending is the same as natural minor (aeolian) 
                 descending: [12, 10, 8, 7, 5, 3, 2, 0]                 descending: [12, 10, 8, 7, 5, 3, 2, 0]
             },             },
Zeile 474: Zeile 589:
         };         };
  
-        // Scale starting notes for easy mode 
         const easyModeStartNotes = {         const easyModeStartNotes = {
-            "ionian": 60, // C4 +            "ionian": 60, 
-            "aeolian": 69, // A4 +            "aeolian": 69, 
-            "harmonic-minor": 69, // A4 +            "harmonic-minor": 69, 
-            "melodic-minor": 69, // A4 +            "melodic-minor": 69, 
-            "dorian": 62, // D4 +            "dorian": 62, 
-            "lydian": 64, // E4 +            "lydian": 64, 
-            "mixolydian": 65, // F4 +            "mixolydian": 65, 
-            "phrygian": 67, // G4 +            "phrygian": 67, 
-            "locrian": 71  // B4+            "locrian": 71
         };         };
  
-        // Keys for intermediate mode (0-3 sharps/flats) 
         const intermediateModeKeys = [         const intermediateModeKeys = [
-            { name: "C", midiBase: 60 },  // C (no sharps/flats) +            { name: "C", midiBase: 60 }, 
-            { name: "G", midiBase: 67 },  // G (1 sharp) +            { name: "G", midiBase: 67 }, 
-            { name: "D", midiBase: 62 },  // D (2 sharps) +            { name: "D", midiBase: 62 }, 
-            { name: "A", midiBase: 69 },  // A (3 sharps) +            { name: "A", midiBase: 69 }, 
-            { name: "F", midiBase: 65 },  // F (1 flat) +            { name: "F", midiBase: 65 }, 
-            { name: "Bb", midiBase: 70 }, // Bb (2 flats) +            { name: "Bb", midiBase: 70 }, 
-            { name: "Eb", midiBase: 63 }  // Eb (3 flats)+            { name: "Eb", midiBase: 63 }
         ];         ];
  
-        // Convert note name to MIDI number 
-        function noteToMidi(note, octave) { 
-            const noteValues = { 
-                "C": 0, "C#": 1, "D": 2, "D#": 3,  
-                "E": 4, "F": 5, "F#": 6, "G": 7,  
-                "G#": 8, "A": 9, "A#": 10, "B": 11 
-            }; 
-             
-            return 12 + (octave * 12) + noteValues[note]; 
-        } 
- 
-        // Get random chromatic note between C3 and C5 
         function getRandomChromatic() {         function getRandomChromatic() {
-            // C3 is MIDI 48, C5 is MIDI 72 
             return Math.floor(Math.random() * 25) + 48;             return Math.floor(Math.random() * 25) + 48;
         }         }
  
-        // Get scale start note based on selected mode 
         function getScaleStartNote(scale) {         function getScaleStartNote(scale) {
             if (currentMode === "easy") {             if (currentMode === "easy") {
                 return easyModeStartNotes[scale];                 return easyModeStartNotes[scale];
             } else if (currentMode === "intermediate") {             } else if (currentMode === "intermediate") {
-                // Choose random key from intermediate keys 
                 const randomKey = intermediateModeKeys[Math.floor(Math.random() * intermediateModeKeys.length)];                 const randomKey = intermediateModeKeys[Math.floor(Math.random() * intermediateModeKeys.length)];
-                // Adjust to ensure it's between C3 and C5 
                 let midiNote = randomKey.midiBase;                 let midiNote = randomKey.midiBase;
                 while (midiNote < 48) midiNote += 12;                 while (midiNote < 48) midiNote += 12;
                 while (midiNote > 72) midiNote -= 12;                 while (midiNote > 72) midiNote -= 12;
                 return midiNote;                 return midiNote;
-            } else { // advanced+            } else {
                 return getRandomChromatic();                 return getRandomChromatic();
             }             }
         }         }
  
-        // Play a note with Web Audio API 
         function playNote(midiNote, time, duration) {         function playNote(midiNote, time, duration) {
             const oscillator = audioContext.createOscillator();             const oscillator = audioContext.createOscillator();
             const gainNode = audioContext.createGain();             const gainNode = audioContext.createGain();
-             
             oscillator.type = 'sine';             oscillator.type = 'sine';
             oscillator.frequency.value = 440 * Math.pow(2, (midiNote - 69) / 12);             oscillator.frequency.value = 440 * Math.pow(2, (midiNote - 69) / 12);
-             
-            // Apply a simple envelope 
             gainNode.gain.setValueAtTime(0, time);             gainNode.gain.setValueAtTime(0, time);
             gainNode.gain.linearRampToValueAtTime(0.5, time + 0.02);             gainNode.gain.linearRampToValueAtTime(0.5, time + 0.02);
             gainNode.gain.linearRampToValueAtTime(0.3, time + 0.1);             gainNode.gain.linearRampToValueAtTime(0.3, time + 0.1);
             gainNode.gain.linearRampToValueAtTime(0, time + duration);             gainNode.gain.linearRampToValueAtTime(0, time + duration);
-             
             oscillator.connect(gainNode);             oscillator.connect(gainNode);
             gainNode.connect(audioContext.destination);             gainNode.connect(audioContext.destination);
-             
             oscillator.start(time);             oscillator.start(time);
             oscillator.stop(time + duration);             oscillator.stop(time + duration);
         }         }
  
-        // Play a scale 
         function playScale(scale, startNoteMidi, callback) {         function playScale(scale, startNoteMidi, callback) {
             initAudioContext();             initAudioContext();
-             
             const scaleData = scaleDefinitions[scale];             const scaleData = scaleDefinitions[scale];
             const noteDuration = 0.4;             const noteDuration = 0.4;
             let time = audioContext.currentTime;             let time = audioContext.currentTime;
-             
-            // Play ascending 
             for (let i = 0; i < scaleData.ascending.length; i++) {             for (let i = 0; i < scaleData.ascending.length; i++) {
                 playNote(startNoteMidi + scaleData.ascending[i], time, noteDuration);                 playNote(startNoteMidi + scaleData.ascending[i], time, noteDuration);
                 time += noteDuration;                 time += noteDuration;
             }             }
-             
-            // Play descending 
             for (let i = 0; i < scaleData.descending.length; i++) {             for (let i = 0; i < scaleData.descending.length; i++) {
-                if (i > 0) { // Skip first note as it's the same as last ascending note+                if (i > 0) {
                     playNote(startNoteMidi + scaleData.descending[i], time, noteDuration);                     playNote(startNoteMidi + scaleData.descending[i], time, noteDuration);
                     time += noteDuration;                     time += noteDuration;
                 }                 }
             }             }
-             
-            // Execute callback after scale has played 
             if (callback) {             if (callback) {
                 setTimeout(callback, (scaleData.ascending.length + scaleData.descending.length - 1) * noteDuration * 1000);                 setTimeout(callback, (scaleData.ascending.length + scaleData.descending.length - 1) * noteDuration * 1000);
Zeile 581: Zeile 664:
         }         }
  
-        // Practice all selected scales 
-        function startPractice() { 
-            if (!isAnyScaleSelected()) { 
-                alert("Bitte wähle mindestens eine Skala aus."); 
-                return; 
-            } 
-             
-            const scales = getSelectedScales(); 
-            initAudioContext(); 
-             
-            let index = 0; 
-            const playNextScale = () => { 
-                if (index < scales.length) { 
-                    const scale = scales[index]; 
-                    const startNote = getScaleStartNote(scale); 
-                    playScale(scale, startNote, () => { 
-                        index++; 
-                        setTimeout(playNextScale, 500); // 500ms pause between scales 
-                    }); 
-                } 
-            }; 
-             
-            playNextScale(); 
-        } 
- 
-        // Initialize quiz 
         function startQuiz() {         function startQuiz() {
             if (!isAnyScaleSelected()) {             if (!isAnyScaleSelected()) {
Zeile 612: Zeile 669:
                 return;                 return;
             }             }
-             
-            // Set up quiz environment 
             document.getElementById("practiceSection").style.display = "none";             document.getElementById("practiceSection").style.display = "none";
             document.getElementById("quizSection").style.display = "block";             document.getElementById("quizSection").style.display = "block";
-             
-            // Reset quiz variables 
             quizInProgress = true;             quizInProgress = true;
             currentQuizQuestion = 0;             currentQuizQuestion = 0;
             correctAnswers = 0;             correctAnswers = 0;
-             
-            // Create quiz questions 
             const selectedScales = getSelectedScales();             const selectedScales = getSelectedScales();
             quizScales = [];             quizScales = [];
-             
-            // Generate 10 random questions from selected scales 
             for (let i = 0; i < totalQuizQuestions; i++) {             for (let i = 0; i < totalQuizQuestions; i++) {
                 const randomScaleIndex = Math.floor(Math.random() * selectedScales.length);                 const randomScaleIndex = Math.floor(Math.random() * selectedScales.length);
-                const scale = selectedScales[randomScaleIndex]; +                quizScales.push(selectedScales[randomScaleIndex]);
-                quizScales.push(scale);+
             }             }
-             
-            // Reset progress markers 
             resetProgressMarkers();             resetProgressMarkers();
-             
-            // Start first question 
             nextQuestion();             nextQuestion();
         }         }
  
-        // Play the current quiz scale 
         function playCurrentQuizScale() {         function playCurrentQuizScale() {
             if (!quizInProgress || currentQuizQuestion >= quizScales.length) return;             if (!quizInProgress || currentQuizQuestion >= quizScales.length) return;
-             
             currentScale = quizScales[currentQuizQuestion];             currentScale = quizScales[currentQuizQuestion];
             const startNote = getScaleStartNote(currentScale);             const startNote = getScaleStartNote(currentScale);
-             
             playScale(currentScale, startNote);             playScale(currentScale, startNote);
-             
-            // Enable play again button 
             document.getElementById("playAgainBtn").style.display = "inline-block";             document.getElementById("playAgainBtn").style.display = "inline-block";
         }         }
  
-        // Show quiz options 
         function showQuizOptions() {         function showQuizOptions() {
             const optionsContainer = document.getElementById("quizOptions");             const optionsContainer = document.getElementById("quizOptions");
             optionsContainer.innerHTML = "";             optionsContainer.innerHTML = "";
-             
-            // Get selected scales for options 
             const scales = getSelectedScales();             const scales = getSelectedScales();
-             
-            // Shuffle the options 
             const shuffledScales = [...scales].sort(() => Math.random() - 0.5);             const shuffledScales = [...scales].sort(() => Math.random() - 0.5);
-             
-            // Create option buttons 
             shuffledScales.forEach(scale => {             shuffledScales.forEach(scale => {
                 const optionElement = document.createElement("div");                 const optionElement = document.createElement("div");
Zeile 670: Zeile 702:
                 optionElement.dataset.scale = scale;                 optionElement.dataset.scale = scale;
                 optionElement.textContent = getScaleName(scale);                 optionElement.textContent = getScaleName(scale);
-                 
                 optionElement.addEventListener("click", function() {                 optionElement.addEventListener("click", function() {
                     if (questionAnswered) return;                     if (questionAnswered) return;
-                     
                     questionAnswered = true;                     questionAnswered = true;
                     checkAnswer(scale);                     checkAnswer(scale);
                 });                 });
-                 
                 optionsContainer.appendChild(optionElement);                 optionsContainer.appendChild(optionElement);
             });             });
         }         }
  
-        // Get human-readable scale name 
         function getScaleName(scale) {         function getScaleName(scale) {
             const scaleNames = {             const scaleNames = {
Zeile 695: Zeile 723:
                 "locrian": "Lokrisch"                 "locrian": "Lokrisch"
             };             };
-             
             return scaleNames[scale] || scale;             return scaleNames[scale] || scale;
         }         }
  
-        // Check the selected answer 
         function checkAnswer(selectedScale) {         function checkAnswer(selectedScale) {
             const isCorrect = selectedScale === currentScale;             const isCorrect = selectedScale === currentScale;
-             
-            // Update progress markers 
             updateProgressMarker(currentQuizQuestion, isCorrect);             updateProgressMarker(currentQuizQuestion, isCorrect);
-             +            if (isCorrect) correctAnswers++;
-            // Update score +
-            if (isCorrect) +
-                correctAnswers++; +
-            } +
-             +
-            // Show visual feedback+
             const options = document.querySelectorAll(".quiz-option");             const options = document.querySelectorAll(".quiz-option");
             options.forEach(option => {             options.forEach(option => {
Zeile 718: Zeile 736:
                 }                 }
             });             });
-             
-            // Show control buttons 
             document.getElementById("playAgainBtn").style.display = "inline-block";             document.getElementById("playAgainBtn").style.display = "inline-block";
             document.getElementById("showAnswerBtn").style.display = isCorrect ? "none" : "inline-block";             document.getElementById("showAnswerBtn").style.display = isCorrect ? "none" : "inline-block";
Zeile 725: Zeile 741:
         }         }
  
-        // Show the correct answer 
         function showCorrectAnswer() {         function showCorrectAnswer() {
             const options = document.querySelectorAll(".quiz-option");             const options = document.querySelectorAll(".quiz-option");
Zeile 733: Zeile 748:
                 }                 }
             });             });
-             
-            // Hide show answer button 
             document.getElementById("showAnswerBtn").style.display = "none";             document.getElementById("showAnswerBtn").style.display = "none";
         }         }
  
-        // Move to the next question 
         function nextQuestion() {         function nextQuestion() {
-            // Reset question state 
             questionAnswered = false;             questionAnswered = false;
-             
-            // Increment question counter 
             currentQuizQuestion++;             currentQuizQuestion++;
-             
-            // Update progress 
             document.getElementById("progressText").textContent = `Frage ${currentQuizQuestion} von ${totalQuizQuestions}`;             document.getElementById("progressText").textContent = `Frage ${currentQuizQuestion} von ${totalQuizQuestions}`;
             document.getElementById("progressFill").style.width = `${(currentQuizQuestion / totalQuizQuestions) * 100}%`;             document.getElementById("progressFill").style.width = `${(currentQuizQuestion / totalQuizQuestions) * 100}%`;
-             
-            // Reset controls 
             document.getElementById("playAgainBtn").style.display = "none";             document.getElementById("playAgainBtn").style.display = "none";
             document.getElementById("showAnswerBtn").style.display = "none";             document.getElementById("showAnswerBtn").style.display = "none";
             document.getElementById("nextQuestionBtn").style.display = "none";             document.getElementById("nextQuestionBtn").style.display = "none";
-             
-            // Check if quiz is complete 
             if (currentQuizQuestion >= totalQuizQuestions) {             if (currentQuizQuestion >= totalQuizQuestions) {
                 finishQuiz();                 finishQuiz();
                 return;                 return;
             }             }
-             
-            // Show options for the next question 
             showQuizOptions();             showQuizOptions();
-             
-            // Play the scale automatically 
             playCurrentQuizScale();             playCurrentQuizScale();
         }         }
  
-        // Finish the quiz and show results 
         function finishQuiz() {         function finishQuiz() {
             quizInProgress = false;             quizInProgress = false;
-             
-            // Hide quiz controls 
             document.getElementById("playScaleBtn").style.display = "none";             document.getElementById("playScaleBtn").style.display = "none";
             document.getElementById("quizOptions").innerHTML = "";             document.getElementById("quizOptions").innerHTML = "";
             document.getElementById("quizInstruction").style.display = "none";             document.getElementById("quizInstruction").style.display = "none";
-             
-            // Show results 
             const resultsElement = document.getElementById("results");             const resultsElement = document.getElementById("results");
             const percentage = Math.round((correctAnswers / totalQuizQuestions) * 100);             const percentage = Math.round((correctAnswers / totalQuizQuestions) * 100);
             resultsElement.textContent = `Quiz beendet! Ergebnis: ${correctAnswers} von ${totalQuizQuestions} richtig (${percentage}%)`;             resultsElement.textContent = `Quiz beendet! Ergebnis: ${correctAnswers} von ${totalQuizQuestions} richtig (${percentage}%)`;
-             
-            // Show restart button 
             const restartBtn = document.createElement("button");             const restartBtn = document.createElement("button");
             restartBtn.textContent = "Neues Quiz starten";             restartBtn.textContent = "Neues Quiz starten";
             restartBtn.addEventListener("click", function() {             restartBtn.addEventListener("click", function() {
-                // Reset UI 
                 document.getElementById("practiceSection").style.display = "block";                 document.getElementById("practiceSection").style.display = "block";
                 document.getElementById("quizSection").style.display = "none";                 document.getElementById("quizSection").style.display = "none";
Zeile 792: Zeile 783:
                 document.getElementById("quizInstruction").style.display = "block";                 document.getElementById("quizInstruction").style.display = "block";
                 document.getElementById("results").textContent = "";                 document.getElementById("results").textContent = "";
-                 
-                // Remove restart button 
                 this.remove();                 this.remove();
             });             });
-             
             resultsElement.appendChild(document.createElement("br"));             resultsElement.appendChild(document.createElement("br"));
             resultsElement.appendChild(restartBtn);             resultsElement.appendChild(restartBtn);
         }         }
  
-        // Initialize progress markers 
         function initializeProgressMarkers() {         function initializeProgressMarkers() {
             const container = document.getElementById("progressMarkers");             const container = document.getElementById("progressMarkers");
             container.innerHTML = "";             container.innerHTML = "";
-             
             for (let i = 0; i < totalQuizQuestions; i++) {             for (let i = 0; i < totalQuizQuestions; i++) {
                 const marker = document.createElement("div");                 const marker = document.createElement("div");
Zeile 814: Zeile 800:
         }         }
  
-        // Reset progress markers 
         function resetProgressMarkers() {         function resetProgressMarkers() {
             const markers = document.querySelectorAll(".progress-marker");             const markers = document.querySelectorAll(".progress-marker");
Zeile 822: Zeile 807:
         }         }
  
-        // Update progress marker 
         function updateProgressMarker(index, isCorrect) {         function updateProgressMarker(index, isCorrect) {
             const markers = document.querySelectorAll(".progress-marker");             const markers = document.querySelectorAll(".progress-marker");
tools/skalentrainer.1747292374.txt.gz · Zuletzt geändert: von Eric Weber