模拟钢琴进阶

进阶了之后,改善了音色,改良了乐谱格式,但需要node.js支持,安装npm install soundfont-player -g
在这里插入图片描述

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>高级钢琴模拟器</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    
    body {
      font-family: 'Arial', sans-serif;
      background-color: #f5f5f5;
      display: flex;
      justify-content: center;
      align-items: center;
      min-height: 100vh;
      padding: 20px;
    }
    
    .piano-container {
      width: 100%;
      max-width: 1000px;
      background-color: #fff;
      border-radius: 10px;
      box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
      padding: 20px;
    }
    
    h1 {
      text-align: center;
      color: #333;
      margin-bottom: 20px;
      font-size: 28px;
    }
    
    .control-panel {
      background-color: #f9f9f9;
      border-radius: 8px;
      padding: 15px;
      margin-bottom: 20px;
    }
    
    h2 {
      font-size: 20px;
      margin-bottom: 10px;
      color: #555;
    }
    
    .control-row {
      display: flex;
      align-items: center;
      margin-bottom: 15px;
      flex-wrap: wrap;
      gap: 10px;
    }
    
    select {
      padding: 10px;
      border-radius: 5px;
      border: 1px solid #ddd;
      background-color: white;
      font-size: 14px;
      flex-grow: 1;
      min-width: 200px;
    }
    
    button {
      background-color: #4a90e2;
      color: white;
      border: none;
      padding: 10px 15px;
      border-radius: 5px;
      cursor: pointer;
      font-size: 14px;
      transition: background-color 0.2s;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    
    button:hover {
      background-color: #357abd;
    }
    
    button.stop {
      background-color: #e25c4a;
    }
    
    button.stop:hover {
      background-color: #c4503e;
    }
    
    #import-button {
      background-color: #4CAF50;
    }
    
    #import-button:hover {
      background-color: #3e8e41;
    }
    
    .music-info {
      margin-top: 15px;
      padding-top: 15px;
      border-top: 1px solid #eee;
    }
    
    #song-title {
      font-size: 18px;
      font-weight: bold;
      margin-bottom: 5px;
    }
    
    #composer {
      font-size: 14px;
      color: #777;
    }
    
    .piano-keyboard {
      display: flex;
      height: 200px;
      position: relative;
      border-radius: 5px;
      overflow: hidden;
      border: 1px solid #ddd;
      user-select: none;
    }
    
    .piano-key {
      position: relative;
      cursor: pointer;
      transition: background-color 0.1s;
    }
    
    .white {
      background-color: #fff;
      height: 100%;
      flex-grow: 1;
      border-right: 1px solid #ddd;
      z-index: 1;
    }
    
    .white:last-child {
      border-right: none;
    }
    
    .white.active {
      background-color: #f0f0f0;
    }
    
    .black {
      position: absolute;
      width: 60%;
      height: 60%;
      background-color: #333;
      z-index: 2;
      border-radius: 0 0 5px 5px;
    }
    
    .black.active {
      background-color: #555;
    }
    
    .note-label {
      position: absolute;
      bottom: 10px;
      width: 100%;
      text-align: center;
      font-size: 12px;
      color: #888;
    }
    
    .loading-message {
      text-align: center;
      margin: 20px 0;
      color: #777;
      font-style: italic;
    }
    
    .visualizer {
      height: 50px;
      background-color: #f0f0f0;
      margin: 20px 0;
      border-radius: 5px;
      overflow: hidden;
    }
    
    .format-info {
      margin-top: 20px;
      background-color: #f9f9f9;
      border-radius: 8px;
      padding: 15px;
    }
    
    .format-info h3 {
      margin-bottom: 10px;
      color: #555;
    }
    
    .format-info pre {
      background-color: #f0f0f0;
      padding: 10px;
      border-radius: 5px;
      overflow-x: auto;
      font-size: 12px;
      margin: 10px 0;
    }
    
    .format-info summary {
      cursor: pointer;
      color: #4a90e2;
      margin: 10px 0;
      font-weight: bold;
    }
    
    .hidden {
      display: none;
    }
    
    #file-input {
      display: none;
    }
    
    /* 文档样式 */
    .documentation {
      margin-top: 20px;
      border-top: 1px solid #eee;
      padding-top: 20px;
    }
    
    .documentation details {
      margin-bottom: 15px;
    }
    
    .documentation summary {
      font-weight: bold;
      cursor: pointer;
      color: #4a90e2;
      margin-bottom: 10px;
    }
    
    /* 为各键分配位置 */
    .key-C {
      left: 0%;
    }
    
    .key-CSharp {
      left: 1.785%;
    }
    
    .key-D {
      left: 3.57%;
    }
    
    .key-DSharp {
      left: 5.355%;
    }
    
    .key-E {
      left: 7.14%;
    }
    
    .key-F {
      left: 10.71%;
    }
    
    .key-FSharp {
      left: 12.495%;
    }
    
    .key-G {
      left: 14.28%;
    }
    
    .key-GSharp {
      left: 16.065%;
    }
    
    .key-A {
      left: 17.85%;
    }
    
    .key-ASharp {
      left: 19.635%;
    }
    
    .key-B {
      left: 21.42%;
    }
  </style>
</head>
<body>
  <div class="piano-container">
    <h1>高级钢琴模拟器</h1>
    
    <div class="control-panel">
      <h2>乐曲选择与控制</h2>
      <div class="control-row">
        <select id="song-select">
          <option value="">-- 请选择乐曲 --</option>
          <option value="moonlight_sonata">贝多芬月光奏鸣曲</option>
          <option value="fur_elise">致爱丽丝</option>
        </select>
        <button id="play-button">播放</button>
        <button id="import-button">添加乐曲</button>
        <input type="file" id="file-input" accept=".json">
      </div>
      
      <div class="music-info">
        <div id="song-title"></div>
        <div id="composer"></div>
      </div>
    </div>
    
    <div id="loading-message" class="loading-message">
      正在加载高品质钢琴音色,请稍候...
    </div>
    
    <div class="visualizer" id="visualizer"></div>
    
    <div class="piano-keyboard" id="piano-keyboard">
      <!-- 钢琴键盘将通过JavaScript生成 -->
    </div>
    
    <div class="documentation">
      <details>
        <summary>乐曲格式说明</summary>
        <div class="format-info">
          <h3>乐曲JSON格式</h3>
          <p>要添加自定义乐曲,请使用以下JSON格式:</p>
          <pre>
{
  "metadata": {
    "title": "乐曲标题",
    "composer": "作曲家",
    "arranger": "编曲者",
    "tempo": 120, // 速度(BPM)
    "timeSignature": [4, 4], // 拍号
    "keySignature": "C" // 调号
  },
  "sections": [
    {
      "name": "主题",
      "tracks": [
        {
          "name": "右手旋律",
          "notes": [
            { "pitch": "C4", "startTime": 0, "duration": 1, "velocity": 0.8 },
            { "pitch": "E4", "startTime": 1, "duration": 0.5, "velocity": 0.7 },
            // 更多音符...
          ]
        },
        {
          "name": "左手伴奏",
          "notes": [
            { "pitch": "C3", "startTime": 0, "duration": 0.5, "velocity": 0.6 },
            { "pitch": "G3", "startTime": 0.5, "duration": 0.5, "velocity": 0.6 },
            // 更多音符...
          ]
        }
      ]
    }
  ],
  "pedals": [
    { "startTime": 0, "endTime": 4, "type": "sustain", "depth": 0.7 },
    // 更多踏板控制...
  ]
}
          </pre>
          <p>格式说明:</p>
          <ul>
            <li><strong>metadata</strong>: 乐曲的基本信息</li>
            <li><strong>sections</strong>: 乐曲的各个部分</li>
            <li><strong>tracks</strong>: 每个部分中的音轨(通常是右手和左手)</li>
            <li><strong>notes</strong>: 各个音符</li>
            <li><strong>pitch</strong>: 音高,使用科学音调记法(如C4为中央C)</li>
            <li><strong>startTime</strong>: 开始时间(以拍为单位)</li>
            <li><strong>duration</strong>: 持续时间(以拍为单位)</li>
            <li><strong>velocity</strong>: 力度(0-1之间的浮点数)</li>
            <li><strong>pedals</strong>: 踏板控制(可选)</li>
          </ul>
        </div>
      </details>
      
      <details>
        <summary>示例乐曲下载</summary>
        <div>
          <p>以下是一些示例乐曲JSON文件,您可以下载并导入到钢琴模拟器中:</p>
          <ul style="margin-top: 10px; margin-bottom: 10px;">
            <li><a href="#" id="canon-link">卡农 (Canon in D)</a></li>
            <li><a href="#" id="clair-de-lune-link">月光 (Clair de Lune)</a></li>
            <li><a href="#" id="butterfly-lovers-link">梁祝 (Butterfly Lovers)</a></li>
          </ul>
          <p><em>点击链接将下载JSON文件,然后您可以使用"添加乐曲"按钮导入。</em></p>
        </div>
      </details>
      
      <details>
        <summary>键盘快捷键</summary>
        <div>
          <p>您可以使用键盘演奏钢琴:</p>
          <ul style="margin-top: 10px;">
            <li><strong>白键</strong>: A, S, D, F, G, H, J, K</li>
            <li><strong>黑键</strong>: W, E, T, Y, U</li>
            <li><strong>空格键</strong>: 播放/停止当前选择的乐曲</li>
          </ul>
        </div>
      </details>
    </div>
  </div>

  <!-- Soundfont-Player 库 -->
  <script src="https://cdn.jsdelivr.net/npm/soundfont-player@0.12.0/dist/soundfont-player.min.js"></script>
  
  <script>
    document.addEventListener('DOMContentLoaded', function() {
      const songSelectElement = document.getElementById('song-select');
      const songTitleElement = document.getElementById('song-title');
      const composerElement = document.getElementById('composer');
      const keyboardElement = document.getElementById('piano-keyboard');
      const loadingMessage = document.getElementById('loading-message');
      const playButton = document.getElementById('play-button');
      const importButton = document.getElementById('import-button');
      const fileInput = document.getElementById('file-input');
      const visualizer = document.getElementById('visualizer');
      
      // 音符信息
      const NOTES = [
        { note: 'C', octave: 3 },
        { note: 'C#', octave: 3 },
        { note: 'D', octave: 3 },
        { note: 'D#', octave: 3 },
        { note: 'E', octave: 3 },
        { note: 'F', octave: 3 },
        { note: 'F#', octave: 3 },
        { note: 'G', octave: 3 },
        { note: 'G#', octave: 3 },
        { note: 'A', octave: 3 },
        { note: 'A#', octave: 3 },
        { note: 'B', octave: 3 },
        { note: 'C', octave: 4 },
        { note: 'C#', octave: 4 },
        { note: 'D', octave: 4 },
        { note: 'D#', octave: 4 },
        { note: 'E', octave: 4 },
        { note: 'F', octave: 4 },
        { note: 'F#', octave: 4 },
        { note: 'G', octave: 4 },
        { note: 'G#', octave: 4 },
        { note: 'A', octave: 4 },
        { note: 'A#', octave: 4 },
        { note: 'B', octave: 4 },
        { note: 'C', octave: 5 },
        { note: 'C#', octave: 5 },
        { note: 'D', octave: 5 },
        { note: 'D#', octave: 5 },
        { note: 'E', octave: 5 },
        { note: 'F', octave: 5 },
        { note: 'F#', octave: 5 },
        { note: 'G', octave: 5 },
        { note: 'G#', octave: 5 },
        { note: 'A', octave: 5 },
        { note: 'A#', octave: 5 },
        { note: 'B', octave: 5 }
      ];
      
      // 全局变量来存储所有乐曲
      const songs = {};
      
      // 钢琴音色类
      class PianoSound {
        constructor() {
          this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
          this.soundFont = null;
          this.analyzer = null;
          this.loadSoundFont();
          this.setupAnalyzer();
        }
        
        setupAnalyzer() {
          this.analyzer = this.audioContext.createAnalyser();
          this.analyzer.fftSize = 2048;
          this.analyzer.connect(this.audioContext.destination);
        }
        
        async loadSoundFont() {
          try {
            this.soundFont = await Soundfont.instrument(this.audioContext, 'acoustic_grand_piano', {
              format: 'mp3',
              soundfont: 'MusyngKite',
              destination: this.analyzer
            });
            loadingMessage.textContent = '钢琴音色加载完成!';
            setTimeout(() => {
              loadingMessage.style.display = 'none';
            }, 2000);
          } catch (error) {
            console.error('音色加载失败:', error);
            loadingMessage.textContent = '音色加载失败,请刷新页面重试。';
            loadingMessage.style.color = 'red';
          }
        }
        
        playNote(note, velocity = 1.0, duration = 1.0) {
          if (this.soundFont) {
            const noteName = this.formatNoteName(note);
            return this.soundFont.play(noteName, this.audioContext.currentTime, {
              duration: duration,
              gain: velocity
            });
          } else {
            console.warn('音色尚未加载完成');
          }
        }
        
        formatNoteName(note) {
          // 将"C4"格式转换为"c4"格式(Soundfont要求小写)
          if (typeof note === 'string') {
            return note.toLowerCase();
          }
          return note;
        }
        
        stopAll() {
          if (this.soundFont) {
            this.soundFont.stop(this.audioContext.currentTime);
          }
        }
        
        resume() {
          if (this.audioContext.state === 'suspended') {
            this.audioContext.resume();
          }
        }
      }
      
      // 乐谱定义 - 贝多芬《月光奏鸣曲》第一乐章开始部分
      const moonlightSonata = {
        metadata: {
          title: "月光奏鸣曲 (第一乐章)",
          composer: "Ludwig van Beethoven",
          arranger: "钢琴简化版",
          tempo: 60,
          timeSignature: [4, 4],
          keySignature: "C#"
        },
        sections: [
          {
            name: "主题",
            tracks: [
              {
                name: "右手旋律",
                notes: [
                  { pitch: "G#4", startTime: 0, duration: 0.5, velocity: 0.7 },
                  { pitch: "C#5", startTime: 0.5, duration: 0.5, velocity: 0.7 },
                  { pitch: "E5", startTime: 1, duration: 0.5, velocity: 0.7 },
                  { pitch: "G#4", startTime: 1.5, duration: 0.5, velocity: 0.7 },
                  { pitch: "C#5", startTime: 2, duration: 0.5, velocity: 0.7 },
                  { pitch: "E5", startTime: 2.5, duration: 0.5, velocity: 0.7 },
                  { pitch: "G#4", startTime: 3, duration: 0.5, velocity: 0.7 },
                  { pitch: "C#5", startTime: 3.5, duration: 0.5, velocity: 0.7 },
                  
                  { pitch: "F#4", startTime: 4, duration: 0.5, velocity: 0.7 },
                  { pitch: "C#5", startTime: 4.5, duration: 0.5, velocity: 0.7 },
                  { pitch: "E5", startTime: 5, duration: 0.5, velocity: 0.7 },
                  { pitch: "F#4", startTime: 5.5, duration: 0.5, velocity: 0.7 },
                  { pitch: "C#5", startTime: 6, duration: 0.5, velocity: 0.7 },
                  { pitch: "E5", startTime: 6.5, duration: 0.5, velocity: 0.7 },
                  { pitch: "F#4", startTime: 7, duration: 0.5, velocity: 0.7 },
                  { pitch: "C#5", startTime: 7.5, duration: 0.5, velocity: 0.7 },
                  
                  { pitch: "G#4", startTime: 8, duration: 0.5, velocity: 0.7 },
                  { pitch: "C#5", startTime: 8.5, duration: 0.5, velocity: 0.7 },
                  { pitch: "E5", startTime: 9, duration: 0.5, velocity: 0.7 },
                  { pitch: "G#4", startTime: 9.5, duration: 0.5, velocity: 0.7 },
                  { pitch: "C#5", startTime: 10, duration: 0.5, velocity: 0.7 },
                  { pitch: "E5", startTime: 10.5, duration: 0.5, velocity: 0.7 },
                  { pitch: "G#4", startTime: 11, duration: 0.5, velocity: 0.7 },
                  { pitch: "C#5", startTime: 11.5, duration: 0.5, velocity: 0.7 },
                  
                  { pitch: "A4", startTime: 12, duration: 0.5, velocity: 0.7 },
                  { pitch: "C#5", startTime: 12.5, duration: 0.5, velocity: 0.7 },
                  { pitch: "E5", startTime: 13, duration: 0.5, velocity: 0.7 },
                  { pitch: "A4", startTime: 13.5, duration: 0.5, velocity: 0.7 },
                  { pitch: "C#5", startTime: 14, duration: 0.5, velocity: 0.7 },
                  { pitch: "E5", startTime: 14.5, duration: 0.5, velocity: 0.7 },
                  { pitch: "A4", startTime: 15, duration: 0.5, velocity: 0.7 },
                  { pitch: "C#5", startTime: 15.5, duration: 0.5, velocity: 0.7 }
                ]
              },
              {
                name: "左手伴奏",
                notes: [
                  { pitch: "C#3", startTime: 0, duration: 2, velocity: 0.6 },
                  { pitch: "G#3", startTime: 2, duration: 2, velocity: 0.6 },
                  
                  { pitch: "A2", startTime: 4, duration: 2, velocity: 0.6 },
                  { pitch: "F#3", startTime: 6, duration: 2, velocity: 0.6 },
                  
                  { pitch: "C#3", startTime: 8, duration: 2, velocity: 0.6 },
                  { pitch: "G#3", startTime: 10, duration: 2, velocity: 0.6 },
                  
                  { pitch: "A2", startTime: 12, duration: 2, velocity: 0.6 },
                  { pitch: "E3", startTime: 14, duration: 2, velocity: 0.6 }
                ]
              }
            ]
          }
        ],
        pedals: [
          { startTime: 0, endTime: 4, type: "sustain", depth: 0.8 },
          { startTime: 4, endTime: 8, type: "sustain", depth: 0.8 },
          { startTime: 8, endTime: 12, type: "sustain", depth: 0.8 },
          { startTime: 12, endTime: 16, type: "sustain", depth: 0.8 }
        ]
      };
      
      // 乐谱定义 - 贝多芬《致爱丽丝》开始部分
      const furElise = {
        metadata: {
          title: "致爱丽丝",
          composer: "Ludwig van Beethoven",
          arranger: "钢琴简化版",
          tempo: 72,
          timeSignature: [3, 8],
          keySignature: "A"
        },
        sections: [
          {
            name: "主题",
            tracks: [
              {
                name: "右手旋律",
                notes: [
                  { pitch: "E5", startTime: 0, duration: 0.5, velocity: 0.8 },
                  { pitch: "D#5", startTime: 0.5, duration: 0.5, velocity: 0.7 },
                  { pitch: "E5", startTime: 1, duration: 0.5, velocity: 0.7 },
                  { pitch: "D#5", startTime: 1.5, duration: 0.5, velocity: 0.7 },
                  { pitch: "E5", startTime: 2, duration: 0.5, velocity: 0.7 },
                  { pitch: "B4", startTime: 2.5, duration: 0.5, velocity: 0.7 },
                  { pitch: "D5", startTime: 3, duration: 0.5, velocity: 0.7 },
                  { pitch: "C5", startTime: 3.5, duration: 0.5, velocity: 0.7 },
                  { pitch: "A4", startTime: 4, duration: 1, velocity: 0.65 },
                  
                  { pitch: "C4", startTime: 5.5, duration: 0.5, velocity: 0.7 },
                  { pitch: "E4", startTime: 6, duration: 0.5, velocity: 0.7 },
                  { pitch: "A4", startTime: 6.5, duration: 0.5, velocity: 0.7 },
                  { pitch: "B4", startTime: 7, duration: 1, velocity: 0.7 },
                  
                  { pitch: "E4", startTime: 8.5, duration: 0.5, velocity: 0.7 },
                  { pitch: "G#4", startTime: 9, duration: 0.5, velocity: 0.7 },
                  { pitch: "B4", startTime: 9.5, duration: 0.5, velocity: 0.7 },
                  { pitch: "C5", startTime: 10, duration: 1, velocity: 0.7 },
                  
                  { pitch: "E4", startTime: 11.5, duration: 0.5, velocity: 0.7 },
                  { pitch: "E5", startTime: 12, duration: 0.5, velocity: 0.8 },
                  { pitch: "D#5", startTime: 12.5, duration: 0.5, velocity: 0.7 },
                  { pitch: "E5", startTime: 13, duration: 0.5, velocity: 0.7 },
                  { pitch: "D#5", startTime: 13.5, duration: 0.5, velocity: 0.7 },
                  { pitch: "E5", startTime: 14, duration: 0.5, velocity: 0.7 },
                  { pitch: "B4", startTime: 14.5, duration: 0.5, velocity: 0.7 },
                  { pitch: "D5", startTime: 15, duration: 0.5, velocity: 0.7 },
                  { pitch: "C5", startTime: 15.5, duration: 0.5, velocity: 0.7 },
                  { pitch: "A4", startTime: 16, duration: 1, velocity: 0.65 }
                ]
              },
              {
                name: "左手伴奏",
                notes: [
                  { pitch: "A2", startTime: 0, duration: 0.5, velocity: 0.6 },
                  { pitch: "E3", startTime: 0.5, duration: 0.5, velocity: 0.6 },
                  { pitch: "A3", startTime: 1, duration: 1, velocity: 0.6 },
                  
                  { pitch: "A2", startTime: 2.5, duration: 0.5, velocity: 0.6 },
                  { pitch: "E3", startTime: 3, duration: 0.5, velocity: 0.6 },
                  { pitch: "A3", startTime: 3.5, duration: 1, velocity: 0.6 },
                  
                  { pitch: "A2", startTime: 5, duration: 0.5, velocity: 0.6 },
                  { pitch: "E3", startTime: 5.5, duration: 0.5, velocity: 0.6 },
                  { pitch: "A3", startTime: 6, duration: 1, velocity: 0.6 },
                  
                  { pitch: "E2", startTime: 8, duration: 0.5, velocity: 0.6 },
                  { pitch: "E3", startTime: 8.5, duration: 0.5, velocity: 0.6 },
                  { pitch: "G#3", startTime: 9, duration: 1, velocity: 0.6 },
                  
                  { pitch: "A2", startTime: 11, duration: 0.5, velocity: 0.6 },
                  { pitch: "E3", startTime: 11.5, duration: 0.5, velocity: 0.6 },
                  { pitch: "A3", startTime: 12, duration: 1, velocity: 0.6 },
                  
                  { pitch: "A2", startTime: 14, duration: 0.5, velocity: 0.6 },
                  { pitch: "E3", startTime: 14.5, duration: 0.5, velocity: 0.6 },
                  { pitch: "A3", startTime: 15, duration: 1, velocity: 0.6 }
                ]
              }
            ]
          }
        ],
        pedals: [
          { startTime: 0, endTime: 4, type: "sustain", depth: 0.5 },
          { startTime: 5, endTime: 8, type: "sustain", depth: 0.5 },
          { startTime: 8, endTime: 11, type: "sustain", depth: 0.5 },
          { startTime: 11, endTime: 16, type: "sustain", depth: 0.5 }
        ]
      };
      
      // 示例乐曲 - 卡农 D大调
      const canonInD = {
        metadata: {
          title: "卡农 D大调",
          composer: "Johann Pachelbel",
          arranger: "钢琴简化版",
          tempo: 70,
          timeSignature: [4, 4],
          keySignature: "D"
        },
        sections: [
          {
            name: "主题",
            tracks: [
              {
                name: "右手旋律",
                notes: [
                  { pitch: "F#5", startTime: 0, duration: 1, velocity: 0.7 },
                  { pitch: "E5", startTime: 1, duration: 1, velocity: 0.7 },
                  { pitch: "D5", startTime: 2, duration: 1, velocity: 0.7 },
                  { pitch: "C#5", startTime: 3, duration: 1, velocity: 0.7 },
                  
                  { pitch: "B4", startTime: 4, duration: 1, velocity: 0.7 },
                  { pitch: "A4", startTime: 5, duration: 1, velocity: 0.7 },
                  { pitch: "B4", startTime: 6, duration: 1, velocity: 0.7 },
                  { pitch: "C#5", startTime: 7, duration: 1, velocity: 0.7 },
                  
                  { pitch: "D5", startTime: 8, duration: 1, velocity: 0.75 },
                  { pitch: "C#5", startTime: 9, duration: 1, velocity: 0.7 },
                  { pitch: "B4", startTime: 10, duration: 1, velocity: 0.7 },
                  { pitch: "A4", startTime: 11, duration: 1, velocity: 0.7 },
                  
                  { pitch: "G4", startTime: 12, duration: 1, velocity: 0.7 },
                  { pitch: "F#4", startTime: 13, duration: 1, velocity: 0.7 },
                  { pitch: "G4", startTime: 14, duration: 1, velocity: 0.7 },
                  { pitch: "E4", startTime: 15, duration: 1, velocity: 0.7 }
                ]
              },
              {
                name: "左手伴奏",
                notes: [
                  { pitch: "D3", startTime: 0, duration: 2, velocity: 0.6 },
                  { pitch: "A3", startTime: 2, duration: 2, velocity: 0.6 },
                  
                  { pitch: "B2", startTime: 4, duration: 2, velocity: 0.6 },
                  { pitch: "F#3", startTime: 6, duration: 2, velocity: 0.6 },
                  
                  { pitch: "G2", startTime: 8, duration: 2, velocity: 0.6 },
                  { pitch: "D3", startTime: 10, duration: 2, velocity: 0.6 },
                  
                  { pitch: "G2", startTime: 12, duration: 2, velocity: 0.6 },
                  { pitch: "A2", startTime: 14, duration: 2, velocity: 0.6 }
                ]
              }
            ]
          }
        ],
        pedals: [
          { startTime: 0, endTime: 4, type: "sustain", depth: 0.6 },
          { startTime: 4, endTime: 8, type: "sustain", depth: 0.6 },
          { startTime: 8, endTime: 12, type: "sustain", depth: 0.6 },
          { startTime: 12, endTime: 16, type: "sustain", depth: 0.6 }
        ]
      };
      
      // 示例乐曲 - 月光 (Clair de Lune)
      const clairDeLune = {
        metadata: {
          title: "月光 (Clair de Lune)",
          composer: "Claude Debussy",
          arranger: "钢琴简化版",
          tempo: 66,
          timeSignature: [9, 8],
          keySignature: "Db"
        },
        sections: [
          {
            name: "主题",
            tracks: [
              {
                name: "右手旋律",
                notes: [
                  { pitch: "Db5", startTime: 0, duration: 1.5, velocity: 0.6 },
                  { pitch: "Bb4", startTime: 1.5, duration: 0.75, velocity: 0.65 },
                  { pitch: "Ab4", startTime: 2.25, duration: 0.75, velocity: 0.6 },
                  
                  { pitch: "Bb4", startTime: 3, duration: 1.5, velocity: 0.65 },
                  { pitch: "Gb4", startTime: 4.5, duration: 0.75, velocity: 0.6 },
                  { pitch: "F4", startTime: 5.25, duration: 0.75, velocity: 0.55 },
                  
                  { pitch: "Eb5", startTime: 6, duration: 1.5, velocity: 0.7 },
                  { pitch: "Db5", startTime: 7.5, duration: 0.75, velocity: 0.65 },
                  { pitch: "Bb4", startTime: 8.25, duration: 0.75, velocity: 0.6 },
                  
                  { pitch: "Ab4", startTime: 9, duration: 2.25, velocity: 0.55 },
                  { pitch: "Bb4", startTime: 11.25, duration: 0.75, velocity: 0.6 },
                  
                  { pitch: "Db5", startTime: 12, duration: 1.5, velocity: 0.65 },
                  { pitch: "Bb4", startTime: 13.5, duration: 0.75, velocity: 0.6 },
                  { pitch: "Ab4", startTime: 14.25, duration: 0.75, velocity: 0.55 }
                ]
              },
              {
                name: "左手伴奏",
                notes: [
                  { pitch: "Db3", startTime: 0, duration: 0.75, velocity: 0.5 },
                  { pitch: "Ab3", startTime: 0.75, duration: 0.75, velocity: 0.5 },
                  { pitch: "F4", startTime: 1.5, duration: 0.75, velocity: 0.5 },
                  { pitch: "Eb4", startTime: 2.25, duration: 0.75, velocity: 0.5 },
                  
                  { pitch: "Eb3", startTime: 3, duration: 0.75, velocity: 0.5 },
                  { pitch: "Bb3", startTime: 3.75, duration: 0.75, velocity: 0.5 },
                  { pitch: "Eb4", startTime: 4.5, duration: 0.75, velocity: 0.5 },
                  { pitch: "Db4", startTime: 5.25, duration: 0.75, velocity: 0.5 },
                  
                  { pitch: "Gb3", startTime: 6, duration: 0.75, velocity: 0.5 },
                  { pitch: "Db4", startTime: 6.75, duration: 0.75, velocity: 0.5 },
                  { pitch: "F4", startTime: 7.5, duration: 0.75, velocity: 0.5 },
                  { pitch: "Eb4", startTime: 8.25, duration: 0.75, velocity: 0.5 },
                  
                  { pitch: "Db3", startTime: 9, duration: 0.75, velocity: 0.5 },
                  { pitch: "Ab3", startTime: 9.75, duration: 0.75, velocity: 0.5 },
                  { pitch: "F4", startTime: 10.5, duration: 0.75, velocity: 0.5 },
                  { pitch: "Eb4", startTime: 11.25, duration: 0.75, velocity: 0.5 },
                  
                  { pitch: "Db3", startTime: 12, duration: 0.75, velocity: 0.5 },
                  { pitch: "Ab3", startTime: 12.75, duration: 0.75, velocity: 0.5 },
                  { pitch: "F4", startTime: 13.5, duration: 0.75, velocity: 0.5 },
                  { pitch: "Eb4", startTime: 14.25, duration: 0.75, velocity: 0.5 }
                ]
              }
            ]
          }
        ],
        pedals: [
          { startTime: 0, endTime: 3, type: "sustain", depth: 0.7 },
          { startTime: 3, endTime: 6, type: "sustain", depth: 0.7 },
          { startTime: 6, endTime: 9, type: "sustain", depth: 0.7 },
          { startTime: 9, endTime: 12, type: "sustain", depth: 0.7 },
          { startTime: 12, endTime: 15, type: "sustain", depth: 0.7 }
        ]
      };
      
      // 示例乐曲 - 梁祝
      const butterflyLovers = {
        metadata: {
          title: "梁祝",
          composer: "何占豪/陈钢",
          arranger: "钢琴简化版",
          tempo: 72,
          timeSignature: [4, 4],
          keySignature: "G"
        },
        sections: [
          {
            name: "主题",
            tracks: [
              {
                name: "右手旋律",
                notes: [
                  { pitch: "D5", startTime: 0, duration: 1, velocity: 0.8 },
                  { pitch: "E5", startTime: 1, duration: 0.5, velocity: 0.8 },
                  { pitch: "G5", startTime: 1.5, duration: 0.5, velocity: 0.8 },
                  { pitch: "A5", startTime: 2, duration: 1, velocity: 0.85 },
                  { pitch: "G5", startTime: 3, duration: 1, velocity: 0.75 },
                  
                  { pitch: "E5", startTime: 4, duration: 1, velocity: 0.75 },
                  { pitch: "D5", startTime: 5, duration: 0.5, velocity: 0.7 },
                  { pitch: "E5", startTime: 5.5, duration: 0.5, velocity: 0.7 },
                  { pitch: "D5", startTime: 6, duration: 1, velocity: 0.75 },
                  { pitch: "B4", startTime: 7, duration: 1, velocity: 0.7 },
                  
                  { pitch: "A4", startTime: 8, duration: 1, velocity: 0.7 },
                  { pitch: "B4", startTime: 9, duration: 0.5, velocity: 0.75 },
                  { pitch: "D5", startTime: 9.5, duration: 0.5, velocity: 0.8 },
                  { pitch: "G5", startTime: 10, duration: 1, velocity: 0.85 },
                  { pitch: "E5", startTime: 11, duration: 1, velocity: 0.8 },
                  
                  { pitch: "D5", startTime: 12, duration: 1.5, velocity: 0.75 },
                  { pitch: "B4", startTime: 13.5, duration: 0.5, velocity: 0.7 },
                  { pitch: "A4", startTime: 14, duration: 1, velocity: 0.7 },
                  { pitch: "G4", startTime: 15, duration: 1, velocity: 0.65 }
                ]
              },
              {
                name: "左手伴奏",
                notes: [
                  { pitch: "G3", startTime: 0, duration: 0.5, velocity: 0.6 },
                  { pitch: "B3", startTime: 0.5, duration: 0.5, velocity: 0.6 },
                  { pitch: "D4", startTime: 1, duration: 0.5, velocity: 0.6 },
                  { pitch: "B3", startTime: 1.5, duration: 0.5, velocity: 0.6 },
                  { pitch: "G3", startTime: 2, duration: 0.5, velocity: 0.6 },
                  { pitch: "B3", startTime: 2.5, duration: 0.5, velocity: 0.6 },
                  { pitch: "D4", startTime: 3, duration: 0.5, velocity: 0.6 },
                  { pitch: "B3", startTime: 3.5, duration: 0.5, velocity: 0.6 },
                  
                  { pitch: "G3", startTime: 4, duration: 0.5, velocity: 0.6 },
                  { pitch: "B3", startTime: 4.5, duration: 0.5, velocity: 0.6 },
                  { pitch: "D4", startTime: 5, duration: 0.5, velocity: 0.6 },
                  { pitch: "B3", startTime: 5.5, duration: 0.5, velocity: 0.6 },
                  { pitch: "G3", startTime: 6, duration: 0.5, velocity: 0.6 },
                  { pitch: "B3", startTime: 6.5, duration: 0.5, velocity: 0.6 },
                  { pitch: "D4", startTime: 7, duration: 0.5, velocity: 0.6 },
                  { pitch: "B3", startTime: 7.5, duration: 0.5, velocity: 0.6 },
                  
                  { pitch: "E3", startTime: 8, duration: 0.5, velocity: 0.6 },
                  { pitch: "G3", startTime: 8.5, duration: 0.5, velocity: 0.6 },
                  { pitch: "B3", startTime: 9, duration: 0.5, velocity: 0.6 },
                  { pitch: "G3", startTime: 9.5, duration: 0.5, velocity: 0.6 },
                  { pitch: "E3", startTime: 10, duration: 0.5, velocity: 0.6 },
                  { pitch: "G3", startTime: 10.5, duration: 0.5, velocity: 0.6 },
                  { pitch: "B3", startTime: 11, duration: 0.5, velocity: 0.6 },
                  { pitch: "G3", startTime: 11.5, duration: 0.5, velocity: 0.6 },
                  
                  { pitch: "D3", startTime: 12, duration: 0.5, velocity: 0.6 },
                  { pitch: "G3", startTime: 12.5, duration: 0.5, velocity: 0.6 },
                  { pitch: "B3", startTime: 13, duration: 0.5, velocity: 0.6 },
                  { pitch: "G3", startTime: 13.5, duration: 0.5, velocity: 0.6 },
                  { pitch: "D3", startTime: 14, duration: 0.5, velocity: 0.6 },
                  { pitch: "G3", startTime: 14.5, duration: 0.5, velocity: 0.6 },
                  { pitch: "B3", startTime: 15, duration: 0.5, velocity: 0.6 },
                  { pitch: "G3", startTime: 15.5, duration: 0.5, velocity: 0.6 }
                ]
              }
            ]
          }
        ],
        pedals: [
          { startTime: 0, endTime: 4, type: "sustain", depth: 0.7 },
          { startTime: 4, endTime: 8, type: "sustain", depth: 0.7 },
          { startTime: 8, endTime: 12, type: "sustain", depth: 0.7 },
          { startTime: 12, endTime: 16, type: "sustain", depth: 0.7 }
        ]
      };
      
      // 音乐播放器类
      class MusicPlayer {
        constructor(pianoSound) {
          this.pianoSound = pianoSound;
          this.isPlaying = false;
          this.currentScore = null;
          this.startTime = 0;
          this.scheduledNotes = [];
          this.tempo = 60;
          this.visualizerInterval = null;
        }
        
        loadScore(musicScore) {
          this.currentScore = musicScore;
          this.tempo = musicScore.metadata.tempo || 60;
          
          // 更新界面信息
          songTitleElement.textContent = musicScore.metadata.title || '未知曲目';
          composerElement.textContent = musicScore.metadata.composer || '未知作者';
          
          console.log(`加载乐曲:${musicScore.metadata.title}`);
        }
        
        play() {
          if (!this.currentScore) {
            alert("请先选择一首乐曲");
            return;
          }
          
          // 确保音频上下文已恢复
          this.pianoSound.resume();
          
          if (this.isPlaying) {
            this.stop();
            return;
          }
          
          this.isPlaying = true;
          this.startTime = this.pianoSound.audioContext.currentTime;
          
          // 更新播放按钮
          playButton.textContent = "停止";
          playButton.classList.add("stop");
          
          // 遍历所有乐段和音轨来调度音符
          this.currentScore.sections.forEach(section => {
            section.tracks.forEach(track => {
              track.notes.forEach(note => {
                // 计算实际开始时间(考虑速度)
                const beatDuration = 60 / this.tempo; // 一拍的时长(秒)
                const startTimeInSeconds = this.startTime + (note.startTime * beatDuration);
                const durationInSeconds = note.duration * beatDuration;
                
                // 调度音符播放
                const scheduledNote = setTimeout(() => {
                  if (this.isPlaying) {
                    const noteWithOctave = note.pitch;
                    const keyElement = document.querySelector(`[data-note="${noteWithOctave}"]`);
                    
                    if (keyElement) {
                      // 为按下的键添加视觉效果
                      keyElement.classList.add('active');
                      setTimeout(() => {
                        keyElement.classList.remove('active');
                      }, durationInSeconds * 1000);
                    }
                    
                    this.pianoSound.playNote(noteWithOctave, note.velocity, durationInSeconds);
                  }
                }, (startTimeInSeconds - this.pianoSound.audioContext.currentTime) * 1000);
                
                this.scheduledNotes.push(scheduledNote);
              });
            });
          });
          
          // 处理踏板
          if (this.currentScore.pedals) {
            this.currentScore.pedals.forEach(pedal => {
              const beatDuration = 60 / this.tempo;
              const startTimeInSeconds = this.startTime + (pedal.startTime * beatDuration);
              const endTimeInSeconds = this.startTime + (pedal.endTime * beatDuration);
              
              // 踏板开始
              setTimeout(() => {
                if (this.isPlaying) {
                  // 控制踏板开始
                  console.log(`踏板开始: ${pedal.type}, 深度: ${pedal.depth}`);
                  // 实际踏板逻辑需要与音频引擎集成
                }
              }, (startTimeInSeconds - this.pianoSound.audioContext.currentTime) * 1000);
              
              // 踏板结束
              setTimeout(() => {
                if (this.isPlaying) {
                  // 控制踏板结束
                  console.log(`踏板结束: ${pedal.type}`);
                }
              }, (endTimeInSeconds - this.pianoSound.audioContext.currentTime) * 1000);
            });
          }
          
          // 启动可视化
          this.startVisualizer();
          
          // 添加乐曲结束回调
          if (this.currentScore.sections && this.currentScore.sections.length > 0) {
            let maxEndTime = 0;
            
            // 找出最后一个音符的结束时间
            this.currentScore.sections.forEach(section => {
              section.tracks.forEach(track => {
                track.notes.forEach(note => {
                  const endTime = note.startTime + note.duration;
                  if (endTime > maxEndTime) {
                    maxEndTime = endTime;
                  }
                });
              });
            });
            
            // 设置乐曲结束的回调
            const songEndTime = this.startTime + (maxEndTime * 60 / this.tempo);
            const timeUntilEnd = (songEndTime - this.pianoSound.audioContext.currentTime) * 1000;
            
            setTimeout(() => {
              if (this.isPlaying) {
                this.stop();
              }
            }, timeUntilEnd + 500); // 加500ms的余量
          }
        }
        
        stop() {
          this.isPlaying = false;
          
          // 更新播放按钮
          playButton.textContent = "播放";
          playButton.classList.remove("stop");
          
          // 清除所有计划播放的音符
          this.scheduledNotes.forEach(noteTimeout => {
            clearTimeout(noteTimeout);
          });
          this.scheduledNotes = [];
          
          // 停止所有正在播放的声音
          this.pianoSound.stopAll();
          
          // 移除所有键盘上的活动状态
          document.querySelectorAll('.piano-key.active').forEach(key => {
            key.classList.remove('active');
          });
          
          // 停止可视化
          this.stopVisualizer();
        }
        
        startVisualizer() {
          // 停止之前的可视化器
          this.stopVisualizer();
          
          // 启动新的可视化器
          const analyser = this.pianoSound.analyzer;
          const bufferLength = analyser.frequencyBinCount;
          const dataArray = new Uint8Array(bufferLength);
          
          const canvas = document.createElement('canvas');
          canvas.width = visualizer.offsetWidth;
          canvas.height = visualizer.offsetHeight;
          visualizer.innerHTML = '';
          visualizer.appendChild(canvas);
          
          const canvasCtx = canvas.getContext('2d');
          
          const draw = () => {
            if (!this.isPlaying) return;
            
            this.visualizerAnimationFrame = requestAnimationFrame(draw);
            
            analyser.getByteFrequencyData(dataArray);
            
            canvasCtx.fillStyle = 'rgb(240, 240, 240)';
            canvasCtx.fillRect(0, 0, canvas.width, canvas.height);
            
            const barWidth = (canvas.width / bufferLength) * 2.5;
            let barHeight;
            let x = 0;
            
            for (let i = 0; i < bufferLength; i++) {
              barHeight = dataArray[i] / 2;
              
              const gradient = canvasCtx.createLinearGradient(0, 0, 0, canvas.height);
              gradient.addColorStop(0, 'rgb(73, 144, 226)');
              gradient.addColorStop(1, 'rgb(110, 175, 255)');
              
              canvasCtx.fillStyle = gradient;
              canvasCtx.fillRect(x, canvas.height - barHeight / 2, barWidth, barHeight);
              
              x += barWidth + 1;
            }
          };
          
          draw();
        }
        
        stopVisualizer() {
          if (this.visualizerAnimationFrame) {
            cancelAnimationFrame(this.visualizerAnimationFrame);
            this.visualizerAnimationFrame = null;
          }
        }
      }
      
      // 初始化钢琴键盘
      function initPianoKeyboard() {
        keyboardElement.innerHTML = '';
        
        // 创建白键和黑键的容器
        const whiteKeysContainer = document.createElement('div');
        whiteKeysContainer.className = 'white-keys';
        whiteKeysContainer.style.display = 'flex';
        whiteKeysContainer.style.width = '100%';
        whiteKeysContainer.style.height = '100%';
        whiteKeysContainer.style.position = 'relative';
        
        // 计算白键数量
        const whiteNotes = ['C', 'D', 'E', 'F', 'G', 'A', 'B'];
        let whiteKeyCount = 0;
        
        NOTES.forEach(noteInfo => {
          if (!noteInfo.note.includes('#')) {
            whiteKeyCount++;
          }
        });
        
        // 创建白键
        NOTES.forEach(noteInfo => {
          const note = noteInfo.note;
          const octave = noteInfo.octave;
          
          if (!note.includes('#')) { // 白键
            const keyElement = document.createElement('div');
            keyElement.className = 'piano-key white';
            keyElement.dataset.note = `${note}${octave}`;
            
            // 添加音符标签
            const noteLabel = document.createElement('div');
            noteLabel.className = 'note-label';
            noteLabel.textContent = `${note}${octave}`;
            keyElement.appendChild(noteLabel);
            
            whiteKeysContainer.appendChild(keyElement);
          }
        });
        
        keyboardElement.appendChild(whiteKeysContainer);
        
        // 现在添加黑键,正确定位它们
        const whiteKeyWidth = 100 / whiteKeyCount;
        let whiteKeyIndex = 0;
        
        NOTES.forEach(noteInfo => {
          const note = noteInfo.note;
          const octave = noteInfo.octave;
          
          if (!note.includes('#')) {
            whiteKeyIndex++;
          } else { // 黑键
            const keyElement = document.createElement('div');
            keyElement.className = 'piano-key black';
            keyElement.dataset.note = `${note}${octave}`;
            
            // 计算黑键位置
            // 根据前一个白键的位置,将黑键定位在白键之间
            const position = (whiteKeyIndex - 0.5) * whiteKeyWidth;
            keyElement.style.left = `${position}%`;
            keyElement.style.width = `${whiteKeyWidth * 0.7}%`;
            
            keyboardElement.appendChild(keyElement);
          }
        });
        
        // 添加钢琴键点击和触摸事件
        const pianoKeys = document.querySelectorAll('.piano-key');
        const pianoSound = new PianoSound();
        
        pianoKeys.forEach(key => {
          const note = key.dataset.note;
          // 鼠标事件
          key.addEventListener('mousedown', () => {
            pianoSound.resume();
            pianoSound.playNote(note, 0.8, 0.5);
            key.classList.add('active');
          });
          
          key.addEventListener('mouseup', () => {
            key.classList.remove('active');
          });
          
          key.addEventListener('mouseleave', () => {
            key.classList.remove('active');
          });
          
          // 触摸事件
          key.addEventListener('touchstart', (e) => {
            e.preventDefault();
            pianoSound.resume();
            pianoSound.playNote(note, 0.8, 0.5);
            key.classList.add('active');
          });
          
          key.addEventListener('touchend', (e) => {
            e.preventDefault();
            key.classList.remove('active');
          });
        });
        
        return pianoSound;
      }
      
      // 初始化示例乐曲
      function initSongs() {
        songs['moonlight_sonata'] = moonlightSonata;
        songs['fur_elise'] = furElise;
        songs['canon_in_d'] = canonInD;
        songs['clair_de_lune'] = clairDeLune;
        songs['butterfly_lovers'] = butterflyLovers;
      }
      
      // 下载示例JSON文件
      function generateJsonDownload(songId, songData) {
        const element = document.getElementById(songId);
        if (!element) return;
        
        element.addEventListener('click', (e) => {
          e.preventDefault();
          
          const jsonString = JSON.stringify(songData, null, 2);
          const blob = new Blob([jsonString], { type: 'application/json' });
          const url = URL.createObjectURL(blob);
          
          const a = document.createElement('a');
          a.href = url;
          a.download = `${songData.metadata.title.replace(/\s+/g, '_')}.json`;
          document.body.appendChild(a);
          a.click();
          document.body.removeChild(a);
          URL.revokeObjectURL(url);
        });
      }
      
      // 主程序
      const pianoSound = initPianoKeyboard();
      const musicPlayer = new MusicPlayer(pianoSound);
      
      // 初始化所有示例乐曲
      initSongs();
      
      // 生成示例JSON下载
      generateJsonDownload('canon-link', canonInD);
      generateJsonDownload('clair-de-lune-link', clairDeLune);
      generateJsonDownload('butterfly-lovers-link', butterflyLovers);
      
      // 绑定歌曲选择事件
      songSelectElement.addEventListener('change', () => {
        const selectedSongId = songSelectElement.value;
        if (selectedSongId && songs[selectedSongId]) {
          musicPlayer.loadScore(songs[selectedSongId]);
        } else {
          songTitleElement.textContent = '';
          composerElement.textContent = '';
          musicPlayer.currentScore = null;
        }
      });
      
      // 绑定播放/停止按钮事件
      playButton.addEventListener('click', () => {
        if (musicPlayer.isPlaying) {
          musicPlayer.stop();
        } else {
          musicPlayer.play();
        }
      });
      
      // 导入自定义乐曲
      importButton.addEventListener('click', () => {
        fileInput.click();
      });
      
      fileInput.addEventListener('change', (e) => {
        const file = e.target.files[0];
        if (!file) return;
        
        const reader = new FileReader();
        reader.onload = (event) => {
          try {
            const jsonData = JSON.parse(event.target.result);
            
            // 简单验证JSON格式
            if (!jsonData.metadata || !jsonData.sections) {
              throw new Error('乐曲格式不正确');
            }
            
            // 生成唯一ID
            const songId = 'custom_' + Date.now();
            songs[songId] = jsonData;
            
            // 添加到下拉菜单
            const option = document.createElement('option');
            option.value = songId;
            option.textContent = jsonData.metadata.title || `自定义乐曲 ${Object.keys(songs).length}`;
            songSelectElement.appendChild(option);
            
            // 选择并加载这首歌
            songSelectElement.value = songId;
            musicPlayer.loadScore(jsonData);
            
            alert(`已成功导入乐曲: ${jsonData.metadata.title || '未命名乐曲'}`);
          } catch (error) {
            alert(`导入失败: ${error.message}`);
            console.error('导入错误:', error);
          }
        };
        reader.readAsText(file);
        
        // 重置文件输入,以便可以重新导入同一文件
        fileInput.value = '';
      });
      
      // 处理键盘事件
      document.addEventListener('keydown', (event) => {
        // 防止重复触发
        if (event.repeat) return;
        
        // 空格键控制播放/停止
        if (event.code === 'Space') {
          event.preventDefault();
          if (musicPlayer.isPlaying) {
            musicPlayer.stop();
          } else {
            musicPlayer.play();
          }
          return;
        }
        
        const keyMap = {
          'a': 'C4', 's': 'D4', 'd': 'E4', 'f': 'F4', 'g': 'G4', 'h': 'A4', 'j': 'B4', 'k': 'C5',
          'w': 'C#4', 'e': 'D#4', 't': 'F#4', 'y': 'G#4', 'u': 'A#4'
        };
        
        const note = keyMap[event.key.toLowerCase()];
        if (note) {
          const keyElement = document.querySelector(`[data-note="${note}"]`);
          if (keyElement) {
            pianoSound.resume();
            pianoSound.playNote(note, 0.8, 0.5);
            keyElement.classList.add('active');
          }
        }
      });
      
      document.addEventListener('keyup', (event) => {
        const keyMap = {
          'a': 'C4', 's': 'D4', 'd': 'E4', 'f': 'F4', 'g': 'G4', 'h': 'A4', 'j': 'B4', 'k': 'C5',
          'w': 'C#4', 'e': 'D#4', 't': 'F#4', 'y': 'G#4', 'u': 'A#4'
        };
        
        const note = keyMap[event.key.toLowerCase()];
        if (note) {
          const keyElement = document.querySelector(`[data-note="${note}"]`);
          if (keyElement) {
            keyElement.classList.remove('active');
          }
        }
      });
      
      // 添加窗口大小调整事件,使可视化器适应新的大小
      window.addEventListener('resize', () => {
        if (musicPlayer.isPlaying) {
          musicPlayer.startVisualizer();
        }
      });
    });
  </script>
</body>
</html>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值