Created
July 13, 2025 20:18
-
-
Save sgqy/a5cb9d7b66f1665ef9032297bf4f0691 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!-- AIGC: Generated by Gemini --> | |
| <!DOCTYPE html> | |
| <html lang="zh"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <title>音符计算练习</title> | |
| <!-- A4 双页打印,每页上下布局,行高调整,sci行浅蓝背景;自动生成Helmholtz题目与完整答案 --> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <!-- 更新:引入 EB Garamond (正文) 和 Noto Music (音乐符号) 字体 --> | |
| <link href="https://fonts.googleapis.com/css2?family=EB+Garamond:wght@500;700&family=Noto+Music&display=swap" rel="stylesheet"> | |
| <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> | |
| <style> | |
| @page { size: A4 portrait; margin: 1cm; } | |
| html, body { margin: 0; padding: 0; height: 100%;} | |
| body { font-family: 'EB Garamond', serif; -webkit-print-color-adjust: exact; print-color-adjust: exact; } | |
| .page { display: flex; flex-direction: column; height: 100%; page-break-after: always; } | |
| .half { flex: 1; display: flex; align-items: center; justify-content: center; } | |
| table { width: 90%; border-collapse: collapse; table-layout: fixed; } | |
| th, td { | |
| border: 1px solid #000; | |
| text-align: center; | |
| font-size: 10pt; | |
| width: 20%; | |
| height: 11mm; | |
| line-height: 1.2; | |
| padding: 1px; | |
| vertical-align: middle; | |
| word-break: break-all; | |
| } | |
| /* 浅色表头背景 */ | |
| thead th:nth-child(1) { background: #f8d7da; } | |
| thead th:nth-child(2) { background: #fff3cd; } | |
| thead th:nth-child(3) { background: #e2e3e5; } | |
| thead th:nth-child(4) { background: #d4edda; } | |
| thead th:nth-child(5) { background: #d1ecf1; } | |
| /* sci 行背景 */ | |
| tbody tr[id^="sci-"] td { background: #eaf4ff; } | |
| .q-note { | |
| text-align: left; | |
| font-size: 14pt; | |
| padding-left: 8px; | |
| font-weight: bold; | |
| } | |
| .music-symbol { | |
| font-family: 'Noto Music', serif; | |
| font-size: 1.2em; | |
| vertical-align: middle; | |
| } | |
| /* 更新:添加仅屏幕显示的样式 */ | |
| .screen-only { | |
| padding: 10px 20px; | |
| background-color: #f0f8ff; | |
| border-bottom: 1px solid #ddd; | |
| margin-bottom: 10px; | |
| } | |
| .screen-only h2 { | |
| margin: 0 0 10px 0; | |
| } | |
| .screen-only p { | |
| margin: 5px 0; | |
| } | |
| /* 更新:添加打印样式,隐藏屏幕专属区域 */ | |
| @media print { | |
| .screen-only { | |
| display: none; | |
| } | |
| .page { | |
| height: 100%; /* 打印时确保页面高度正确 */ | |
| } | |
| } | |
| </style> | |
| <script> | |
| $(function() { | |
| // --- 音乐理论核心数据 --- | |
| const perTable = 5; | |
| const NOTE_MIDI_OFFSETS = { 'C': 0, 'D': 2, 'E': 4, 'F': 5, 'G': 7, 'A': 9, 'B': 11 }; | |
| const MIDI_NOTE_NAMES = { | |
| 0: [{ l: 'C', a: '' }, { l: 'B', a: '#' }, { l: 'D', a: 'bb' }], | |
| 1: [{ l: 'C', a: '#' }, { l: 'D', a: 'b' }], | |
| 2: [{ l: 'D', a: '' }, { l: 'C', a: 'x' }, { l: 'E', a: 'bb' }], | |
| 3: [{ l: 'D', a: '#' }, { l: 'E', a: 'b' }], | |
| 4: [{ l: 'E', a: '' }, { l: 'F', a: 'b' }, { l: 'D', a: 'x' }], | |
| 5: [{ l: 'F', a: '' }, { l: 'E', a: '#' }], | |
| 6: [{ l: 'F', a: '#' }, { l: 'G', a: 'b' }], | |
| 7: [{ l: 'G', a: '' }, { l: 'F', a: 'x' }, { l: 'A', a: 'bb' }], | |
| 8: [{ l: 'G', a: '#' }, { l: 'A', a: 'b' }], | |
| 9: [{ l: 'A', a: '' }, { l: 'G', a: 'x' }, { l: 'B', a: 'bb' }], | |
| 10: [{ l: 'A', a: '#' }, { l: 'B', a: 'b' }], | |
| 11: [{ l: 'B', a: '' }, { l: 'C', a: 'b' }, { l: 'A', a: 'x' }] | |
| }; | |
| const ACCIDENTAL_SYMBOLS = { 'bb': '\u{1D12B}', 'b': '\u266D', '': '', '#': '\u266F', 'x': '\u{1D12A}' }; | |
| const ACCIDENTAL_MODIFIERS = { 'bb': -2, 'b': -1, '': 0, '#': 1, 'x': 2 }; | |
| const COLUMN_ACCIDENTALS = ['bb', 'b', '', '#', 'x']; | |
| // --- 核心转换与生成函数 --- | |
| function wrapSymbol(symbol) { | |
| if (!symbol) return ''; | |
| return `<span class="music-symbol">${symbol}</span>`; | |
| } | |
| function scientificToHelmholtz(letter, octave) { | |
| if (octave >= 3) { | |
| const sup = octave - 3; | |
| return letter.toLowerCase() + (sup > 0 ? `<sup>${sup}</sup>` : ''); | |
| } else { | |
| const sub = 2 - octave; | |
| return letter.toUpperCase() + (sub > 0 ? `<sub>${sub}</sub>` : ''); | |
| } | |
| } | |
| function midiToAllNotations(midi) { | |
| if (midi < 12 || midi > 108) return { sci: ['N/A'], helm: ['N/A'] }; | |
| const octave = Math.floor(midi / 12) - 1; | |
| const noteIndex = midi % 12; | |
| const possibleNotes = MIDI_NOTE_NAMES[noteIndex] || []; | |
| const notations = { sci: [], helm: [] }; | |
| possibleNotes.forEach(note => { | |
| const accSymbol = ACCIDENTAL_SYMBOLS[note.a] || ''; | |
| notations.sci.push(wrapSymbol(accSymbol) + note.l + octave); | |
| const helmBase = scientificToHelmholtz(note.l, octave); | |
| notations.helm.push(wrapSymbol(accSymbol) + helmBase); | |
| }); | |
| return notations; | |
| } | |
| function parseHelmholtz(helmNoteHtml) { | |
| const textOnly = $('<div>').html(helmNoteHtml).text(); | |
| let notePart = textOnly; | |
| let initialModifier = 0; | |
| const orderedAccKeys = ['bb', 'x', 'b', '#']; | |
| for (const key of orderedAccKeys) { | |
| const symbol = ACCIDENTAL_SYMBOLS[key]; | |
| if (textOnly.startsWith(symbol)) { | |
| initialModifier = ACCIDENTAL_MODIFIERS[key]; | |
| notePart = textOnly.substring(symbol.length); | |
| break; | |
| } | |
| } | |
| const letter = notePart.match(/[A-Ga-g]/)[0]; | |
| const supMatch = helmNoteHtml.match(/<sup>(\d+)<\/sup>/); | |
| const subMatch = helmNoteHtml.match(/<sub>(\d+)<\/sub>/); | |
| let octave; | |
| if (letter === letter.toLowerCase()) { | |
| octave = 3 + (supMatch ? parseInt(supMatch[1]) : 0); | |
| } else { | |
| octave = 2 - (subMatch ? parseInt(subMatch[1]) : 0); | |
| } | |
| const baseMidi = 12 * (octave + 1) + NOTE_MIDI_OFFSETS[letter.toUpperCase()]; | |
| return baseMidi + initialModifier; | |
| } | |
| function genAlteredHelmholtz(generated) { | |
| let note; | |
| const letters = ['A', 'B', 'C', 'D', 'E', 'F', 'G']; | |
| const initialAccidentals = ['bb', 'b', '', '#', 'x']; | |
| do { | |
| const useLower = Math.random() > 0.3; | |
| const letter = letters[Math.floor(Math.random() * letters.length)]; | |
| let octave, helmBase; | |
| if (useLower) { | |
| octave = Math.floor(Math.random() * 3) + 3; | |
| } else { | |
| octave = Math.floor(Math.random() * 3); | |
| } | |
| helmBase = scientificToHelmholtz(letter, octave); | |
| const accKey = initialAccidentals[Math.floor(Math.random() * initialAccidentals.length)]; | |
| const accSymbol = ACCIDENTAL_SYMBOLS[accKey]; | |
| note = wrapSymbol(accSymbol) + helmBase; | |
| } while (generated.has(note)); | |
| generated.add(note); | |
| return note; | |
| } | |
| // --- 主执行逻辑 --- | |
| $('th').each(function() { | |
| const text = $(this).text(); | |
| if (text) { | |
| $(this).html(wrapSymbol(text)); | |
| } | |
| }); | |
| const generatedQuestions = new Set(); | |
| for (let i = 0; i < perTable * 2; i++) { | |
| const tableId = i < perTable ? 1 : 2; | |
| const rowIndex = (i % perTable) + 1; | |
| const baseHelmholtzQ = genAlteredHelmholtz(generatedQuestions); | |
| const baseMidi = parseHelmholtz(baseHelmholtzQ); | |
| $(`#q-table-${tableId} #hel-${rowIndex} td:nth-child(3)`).addClass('q-note').html(baseHelmholtzQ); | |
| COLUMN_ACCIDENTALS.forEach((colAccKey, colIndex) => { | |
| const columnModifier = ACCIDENTAL_MODIFIERS[colAccKey]; | |
| const targetMidi = baseMidi + columnModifier; | |
| const allNotations = midiToAllNotations(targetMidi); | |
| const column = colIndex + 1; | |
| $(`#a-table-${tableId} #hel-${rowIndex} td:nth-child(${column})`).html(allNotations.helm.join(' / ')); | |
| $(`#a-table-${tableId} #sci-${rowIndex} td:nth-child(${column})`).html(allNotations.sci.join(' / ')); | |
| }); | |
| } | |
| }); | |
| </script> | |
| </head> | |
| <body> | |
| <!-- 更新:添加仅屏幕显示的说明区域 --> | |
| <div class="screen-only"> | |
| <h2>音符计算练习</h2> | |
| <p>在每个格子里填写等音的所有符号。第一行使用大字小字记法,第二行使用科学记法。</p> | |
| <p>使用浏览器的打印功能(Ctrl+P)双面打印在纸上使用,不要截图后打印。</p> | |
| </div> | |
| <!-- 第1页 --> | |
| <div class="page"> | |
| <!-- 习题 --> | |
| <div class="half"> | |
| <table id="q-table-1"> | |
| <thead> | |
| <tr><th>𝄫</th><th>♭</th><th>♮</th><th>♯</th><th>𝄪</th></tr> | |
| </thead> | |
| <tbody> | |
| <tr id="hel-1"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="sci-1"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="hel-2"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="sci-2"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="hel-3"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="sci-3"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="hel-4"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="sci-4"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="hel-5"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="sci-5"><td></td><td></td><td></td><td></td><td></td></tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| <!-- 答案 --> | |
| <div class="half"> | |
| <table id="a-table-1"> | |
| <thead> | |
| <tr><th>𝄫</th><th>♭</th><th>♮</th><th>♯</th><th>𝄪</th></tr> | |
| </thead> | |
| <tbody> | |
| <tr id="hel-1"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="sci-1"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="hel-2"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="sci-2"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="hel-3"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="sci-3"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="hel-4"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="sci-4"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="hel-5"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="sci-5"><td></td><td></td><td></td><td></td><td></td></tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| <!-- 第2页 --> | |
| <div class="page"> | |
| <!-- 习题 --> | |
| <div class="half"> | |
| <table id="q-table-2"> | |
| <thead> | |
| <tr><th>𝄫</th><th>♭</th><th>♮</th><th>♯</th><th>𝄪</th></tr> | |
| </thead> | |
| <tbody> | |
| <tr id="hel-1"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="sci-1"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="hel-2"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="sci-2"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="hel-3"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="sci-3"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="hel-4"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="sci-4"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="hel-5"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="sci-5"><td></td><td></td><td></td><td></td><td></td></tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| <!-- 答案 --> | |
| <div class="half"> | |
| <table id="a-table-2"> | |
| <thead> | |
| <tr><th>𝄫</th><th>♭</th><th>♮</th><th>♯</th><th>𝄪</th></tr> | |
| </thead> | |
| <tbody> | |
| <tr id="hel-1"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="sci-1"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="hel-2"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="sci-2"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="hel-3"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="sci-3"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="hel-4"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="sci-4"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="hel-5"><td></td><td></td><td></td><td></td><td></td></tr> | |
| <tr id="sci-5"><td></td><td></td><td></td><td></td><td></td></tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment