钢琴模拟器

在这里插入图片描述
在这里插入图片描述

文章目录

    • 钢琴模拟器
        • 代码结构
            • HTML结构
            • CSS样式
            • JavaScript功能
        • 源码
        • 效果图

钢琴模拟器

代码结构
HTML结构

<html>: HTML文档的根元素。
<head>: 包含文档的元数据。
<base>: 指定相对URL的基准。
<title>: 指定页面的标题。
<style>: 包含嵌入的CSS样式。
<body>: 包含文档的内容。
<div class=“container”>: 容器元素,包含主要内容。
<div class=“controls”>: 控件区域,包含选择框、按钮和移调控制。
<select id=“instrument-select”>: 乐器选择框。
<button id=“record”>: 录音按钮。
<button id=“play”>: 播放按钮。
<button id=“stop”>: 停止按钮。
<div class=“transpose-controls”>: 移调控制区域。
<button id=“transpose-down”>: 移调降低按钮。
<input type=“text” id=“transpose-value” readonly>: 显示当前移调值。
<button id=“transpose-up”>: 移调升高按钮。
<div id=“current-instrument”>: 当前乐器显示区域。
<div id=“keyboard”>: 键盘区域。
<div id=“chord-pads”>: 和弦按钮区域。
<div id=“loading”>: 加载提示。

CSS样式

body, html: 设置页面的基本样式。
.container: 设置容器的样式。
.controls: 设置控件区域的样式。
select, button, input: 设置选择框、按钮和输入框的样式。
#instrument-select: 设置乐器选择框的样式。
#current-instrument, #transpose-value: 设置当前乐器和移调值的样式。
.transpose-controls: 设置移调控制区域的样式。
#keyboard: 设置键盘区域的样式。
.key: 设置键的样式。
.key.black: 设置黑键的样式。
#loading: 设置加载提示的样式。
#chord-pads: 设置和弦按钮区域的样式。
.chord-pad: 设置和弦按钮的样式。

JavaScript功能

时钟更新:
updateClock: 更新时钟的时间和日期。
setInterval(updateClock, 1000): 每秒更新一次时钟。
updateClock(): 初次加载时立即更新时钟。
点击事件:
监听乐器选择框、录音按钮、播放按钮和停止按钮的点击事件。
根据点击的元素执行相应的操作(如录音、播放、停止等)。
键盘功能:
createKeyboard: 创建键盘。
createChordPads: 创建和弦按钮。
loadSoundFonts: 加载SoundFont。
loadSoundFont: 加载指定的SoundFont。
transposeNote: 移调音符。
playNote: 播放音符。
releaseNote: 释放音符。
playChord: 播放和弦。
releaseChord: 释放和弦。
startRecording: 开始录音。
stopRecording: 停止录音。
playRecording: 播放录音。
stopPlayback: 停止播放。
updateTransposeDisplay: 更新移调显示。

源码
<html><head><base href="https://websim.ai/app/soundfont-keyboard"/><title>SoundFont Keyboard: Interactive Musical Experience with Chords</title>
<style>body {margin: 0;padding: 0;overflow: hidden;font-family: 'Arial', sans-serif;background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);color: #fff;display: flex;flex-direction: column;justify-content: center;align-items: center;min-height: 100vh;}.container {background: rgba(255, 255, 255, 0.1);border-radius: 20px;padding: 30px;box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);backdrop-filter: blur(4px);border: 1px solid rgba(255, 255, 255, 0.18);display: flex;flex-direction: column;align-items: center;margin-bottom: 20px;}.controls {display: flex;flex-wrap: wrap;justify-content: center;margin-bottom: 20px;}select, button, input {margin: 5px;padding: 10px 15px;font-size: 14px;background-color: rgba(255, 255, 255, 0.2);color: #fff;border: none;border-radius: 5px;cursor: pointer;transition: background-color 0.3s ease;}select:hover, button:hover {background-color: rgba(255, 255, 255, 0.3);}#instrument-select {width: 200px;appearance: none;-webkit-appearance: none;-moz-appearance: none;background-image: url('data:image/svg+xml;utf8,<svg fill="%23ffffff" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M7 10l5 5 5-5z"/><path d="M0 0h24v24H0z" fill="none"/></svg>');background-repeat: no-repeat;background-position-x: 95%;background-position-y: 50%;}#instrument-select option {background-color: #2a2a2a;color: #fff;}#current-instrument, #transpose-value {margin-top: 10px;font-style: italic;}.transpose-controls {display: flex;align-items: center;margin-top: 10px;}.transpose-controls button {width: 30px;height: 30px;padding: 0;font-size: 18px;line-height: 1;}#transpose-value {margin: 0 10px;width: 40px;text-align: center;background-color: rgba(255, 255, 255, 0.1);}#keyboard {display: flex;justify-content: center;background: linear-gradient(to bottom, #4a4a4a, #2a2a2a);padding: 20px;border-radius: 10px;box-shadow: 0 4px 15px rgba(0, 0, 0, 0.5);}.key {width: 40px;height: 150px;background-color: #f0f0f0;border: 1px solid #000;margin: 0 2px;cursor: pointer;border-radius: 0 0 5px 5px;box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);transition: background-color 0.1s ease;}.key.black {width: 30px;height: 100px;background-color: #000;margin-left: -15px;margin-right: -15px;z-index: 1;}.key:active, .key.active {background-color: #ddd;}.key.black:active, .key.black.active {background-color: #333;}#loading {position: fixed;top: 50%;left: 50%;transform: translate(-50%, -50%);font-size: 24px;background: rgba(0, 0, 0, 0.7);padding: 20px;border-radius: 10px;z-index: 20;}#chord-pads {display: flex;flex-wrap: wrap;justify-content: center;max-width: 600px;margin-top: 20px;}.chord-pad {width: 60px;height: 60px;margin: 5px;font-size: 16px;font-weight: bold;background-color: rgba(255, 255, 255, 0.2);border: none;border-radius: 50%;cursor: pointer;transition: background-color 0.3s ease, transform 0.1s ease;}.chord-pad:hover {background-color: rgba(255, 255, 255, 0.3);}.chord-pad:active {transform: scale(0.95);}
</style>
</head>
<body><div class="container"><div class="controls"><select id="instrument-select"><option value="">Select an instrument...</option></select><button id="record">Record</button><button id="play">Play</button><button id="stop">Stop</button><div class="transpose-controls"><button id="transpose-down">-</button><input type="text" id="transpose-value" value="0" readonly><button id="transpose-up">+</button></div></div><div id="current-instrument"></div><div id="keyboard"></div></div><div id="chord-pads"></div><div id="loading">Loading SoundFonts...</div><script src="https://cdnjs.cloudflare.com/ajax/libs/howler/2.2.3/howler.min.js"></script><script>const keys = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];const keyMapping = {'z': 'C3', 's': 'C#3', 'x': 'D3', 'd': 'D#3', 'c': 'E3', 'v': 'F3', 'g': 'F#3','b': 'G3', 'h': 'G#3', 'n': 'A3', 'j': 'A#3', 'm': 'B3','q': 'C4', '2': 'C#4', 'w': 'D4', '3': 'D#4', 'e': 'E4', 'r': 'F4', '5': 'F#4','t': 'G4', '6': 'G#4', 'y': 'A4', '7': 'A#4', 'u': 'B4','i': 'C5', '9': 'C#5', 'o': 'D5', '0': 'D#5', 'p': 'E5', '[': 'F5', '=': 'F#5',']': 'G5'};const octaves = 3;let currentInstrument = 'acoustic_grand_piano';let soundFont;let recording = false;let recordedNotes = [];let startTime;let playbackTimeouts = [];let transposeValue = 0;const pressedKeys = new Set();const chords = {'E': ['E4', 'G#4', 'B4'],'A': ['A3', 'C#4', 'E4'],'F': ['F3', 'A3', 'C4'],'D': ['D4', 'F#4', 'A4'],'G': ['G3', 'B3', 'D4'],'C': ['C4', 'E4', 'G4'],'B': ['B3', 'D#4', 'F#4'],'Em': ['E4', 'G4', 'B4'],'Am': ['A3', 'C4', 'E4'],'Dm': ['D4', 'F4', 'A4'],'Bm': ['B3', 'D4', 'F#4'],'Cm': ['C4', 'D#4', 'G4'],'Fm': ['F3', 'G#3', 'C4'],'E7': ['E4', 'G#4', 'B4', 'D5'],'A7': ['A3', 'C#4', 'E4', 'G4'],'D7': ['D4', 'F#4', 'A4', 'C5'],'G7': ['G3', 'B3', 'D4', 'F4'],'C7': ['C4', 'E4', 'G4', 'A#4']};function createKeyboard() {const keyboard = document.getElementById('keyboard');for (let octave = 3; octave < 3 + octaves; octave++) {keys.forEach((note) => {const key = document.createElement('div');key.className = `key ${note.includes('#') ? 'black' : 'white'}`;key.dataset.note = `${note}${octave}`;key.addEventListener('mousedown', () => playNote(`${note}${octave}`));key.addEventListener('mouseup', () => releaseNote(`${note}${octave}`));key.addEventListener('mouseleave', () => releaseNote(`${note}${octave}`));keyboard.appendChild(key);});}}function createChordPads() {const chordPads = document.getElementById('chord-pads');Object.keys(chords).forEach(chordName => {const pad = document.createElement('button');pad.className = 'chord-pad';pad.textContent = chordName;pad.addEventListener('mousedown', () => playChord(chordName));pad.addEventListener('mouseup', () => releaseChord(chordName));pad.addEventListener('mouseleave', () => releaseChord(chordName));chordPads.appendChild(pad);});}createKeyboard();createChordPads();async function loadSoundFonts() {const response = await fetch('https://gleitz.github.io/midi-js-soundfonts/MusyngKite/names.json');const instruments = await response.json();const select = document.getElementById('instrument-select');instruments.forEach(instrument => {const option = document.createElement('option');option.value = instrument;option.textContent = instrument.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase());select.appendChild(option);});loadSoundFont('acoustic_grand_piano');}async function loadSoundFont(instrument) {document.getElementById('loading').style.display = 'block';currentInstrument = instrument;const response = await fetch(`https://gleitz.github.io/midi-js-soundfonts/MusyngKite/${instrument}-mp3.js`);const soundFontData = await response.text();eval(soundFontData);soundFont = MIDI.Soundfont[instrument];document.getElementById('loading').style.display = 'none';document.getElementById('current-instrument').textContent = `Current Instrument: ${instrument.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}`;}function transposeNote(note) {const [noteName, octave] = [note.slice(0, -1), parseInt(note.slice(-1))];let noteIndex = keys.indexOf(noteName);noteIndex += transposeValue;let newOctave = octave + Math.floor(noteIndex / 12);noteIndex = (noteIndex + 12) % 12; // Ensure positive indexreturn `${keys[noteIndex]}${newOctave}`;}function playNote(note) {if (!soundFont) return;const transposedNote = transposeNote(note);let sound = new Howl({src: [soundFont[transposedNote]],format: ['mp3']});sound.play();if (recording) {const time = Date.now() - startTime;recordedNotes.push({ note, time });}// Highlight the keyconst key = document.querySelector(`.key[data-note="${note}"]`);if (key) {key.classList.add('active');}}function releaseNote(note) {// Remove highlight from the keyconst key = document.querySelector(`.key[data-note="${note}"]`);if (key) {key.classList.remove('active');}}function playChord(chordName) {if (!soundFont) return;chords[chordName].forEach(note => {playNote(note);});if (recording) {const time = Date.now() - startTime;recordedNotes.push({ chord: chordName, time });}}function releaseChord(chordName) {chords[chordName].forEach(note => {releaseNote(note);});}function startRecording() {recording = true;recordedNotes = [];startTime = Date.now();document.getElementById('record').textContent = 'Stop Recording';}function stopRecording() {recording = false;document.getElementById('record').textContent = 'Record';}function playRecording() {if (recordedNotes.length === 0) return;stopPlayback(); // Stop any ongoing playbackconst playbackStartTime = Date.now();recordedNotes.forEach(({ note, chord, time }) => {const timeout = setTimeout(() => {if (note) {playNote(note);setTimeout(() => releaseNote(note), 200);} else if (chord) {playChord(chord);setTimeout(() => releaseChord(chord), 200);}}, time);playbackTimeouts.push(timeout);});}function stopPlayback() {// Clear all scheduled playback timeoutsplaybackTimeouts.forEach(timeout => clearTimeout(timeout));playbackTimeouts = [];
""// Stop all currently playing soundsHowler.stop();// Reset all key colorsdocument.querySelectorAll('.key').forEach(key => {key.classList.remove('active');});}function updateTransposeDisplay() {const transposeInput = document.getElementById('transpose-value');transposeInput.value = transposeValue >= 0 ? `+${transposeValue}` : transposeValue;}loadSoundFonts();document.getElementById('instrument-select').addEventListener('change', (e) => loadSoundFont(e.target.value));document.getElementById('record').addEventListener('click', () => {if (recording) {stopRecording();} else {startRecording();}});document.getElementById('play').addEventListener('click', playRecording);document.getElementById('stop').addEventListener('click', stopPlayback);document.getElementById('transpose-down').addEventListener('click', () => {transposeValue = Math.max(transposeValue - 1, -12);updateTransposeDisplay();});document.getElementById('transpose-up').addEventListener('click', () => {transposeValue = Math.min(transposeValue + 1, 12);updateTransposeDisplay();});window.addEventListener('keydown', (e) => {const note = keyMapping[e.key.toLowerCase()];if (note && !pressedKeys.has(note)) {pressedKeys.add(note);playNote(note);}});window.addEventListener('keyup', (e) => {const note = keyMapping[e.key.toLowerCase()];if (note) {pressedKeys.delete(note);releaseNote(note);}});// Initialize transpose displayupdateTransposeDisplay();</script>
</body></html>
效果图

在这里插入图片描述

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3281537.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

Linux中进程间通信和理解管道

管道文件为内存及文件&#xff0c;没有名称&#xff08;匿名管道&#xff09; 如何让两个进程看到同一个管道文件&#xff1f; 通过fork创建子进程完成。 匿名管道可以用于父子进程之间的通信。 匿名管道是一个固定大小的缓冲区&#xff0c;写端写满后就会阻塞&#xff0c;…

Python自动发送邮件如何设置邮件内容格式?

Python自动发送邮件时&#xff0c;如何自动化发送HTML格式邮件&#xff1f; Python是一种功能强大且灵活的编程语言&#xff0c;广泛用于各种自动化任务&#xff0c;其中包括自动发送邮件。AokSend将介绍在使用Python自动发送邮件时&#xff0c;如何设置邮件内容的格式&#x…

Mongodb集合操作

文章目录 1、进入容器2、如果数据库不存在&#xff0c;则创建数据库&#xff0c;否则切换到指定数据库3、在 MongoDB 中&#xff0c;创建集合不是必须操作。当你插入一些文档时&#xff0c;MongoDB 会自动创建集合。4、查看数据库列表5、查看集合6、显示创建集合7、删除集合 1、…

创新突破 | OpenCSG发布StarShip CodeReview v1.0.0 Beta版

1. 代码审查很关键但耗时耗力 在软件开发过程中&#xff0c;代码审查是确保代码质量的关键环节。代码审查有助于维护代码标准和发现潜在错误&#xff0c;但也常常耗费大量时间和精力。审查者不仅需要深入理解代码逻辑&#xff0c;还要在繁复的逻辑中识别Bug&#xff0c;这个过…

Python_Flask学习笔记

1.配置 查询字符串的形式传参 app.route(/book/list) def book_list():page request.args.get(page,default1,typeint)return f"您获取的是{page}的图书列表&#xff01;"if __name__ __main__:app.run()3.HTML模版渲染 from flask import Flask,render_templa…

C#知识|对象序列化与反序列化

哈喽,你好啊,我是雷工! 前面练习了将数据存储到TXT文件,今天接着学习对象系列化与XML文件基础的应用, 以下为学习笔记。 01 对象的序列化 序列化是在开发中非常常见的,无论是WinForm开发,还是Web开发都会用得到序列化。 1.1、文本保存对象的缺点 txt文件可以用记事本直…

【p-export-excel】一个轻松实现Excel文件导出的JavaScript插件

p-export-excel&#xff08;github&#xff1a;https://github.com/pbstar/p-export-excel&#xff09;是一个功能强大的JavaScript插件&#xff0c;专门用于导出Excel文件。它支持xlsx和csv两种格式&#xff0c;且提供了丰富的配置选项&#xff0c;允许开发者根据实际需求进行…

贝斯和吉他的区别,怎么打贝斯谱子?一来看看贝斯和吉他的区别,怎么打贝斯谱子的相关内容

在音乐的浩瀚宇宙中&#xff0c;贝斯与吉他作为两大支柱性乐器&#xff0c;虽外观相仿&#xff0c;实则内在差异显著&#xff0c;各自以独特的音色与演奏技法在乐队中发挥着不可或缺的作用。 首先&#xff0c;从音乐功能来看&#xff0c;贝斯以其深沉有力的低音线条&#xff0c…

飞书打卡 快捷指令

使用快捷指令定时飞书打卡 在网上找了一圈&#xff0c;只有钉钉打卡的快捷指令&#xff0c;但是公司换飞书&#xff0c;哪个打工人不怕忘记打卡呢&#xff0c;所以自己研究了一下&#xff0c;其实也很简单。 找url 问题的最关键是打开飞书的打卡界面 如果只是打开飞书APP 很…

iPhone 上的误删的联系人去哪了?如何从 iPhone 上找回联系人的 4 种方法

如果您丢失了所有 iPhone 联系人&#xff0c;那将是一场灾难&#xff0c;因为许多人没有记住电话号码的习惯。iPhone 联系人恢复可以让整个过程变得更容易。如果您仍然对如何在 iPhone 上恢复联系人感到困惑&#xff0c;您可以找到有关 5 个出色解决方案的更多详细信息。只需阅…

C++11 可变参数模板

C11的新特性可变参数模板能够创建可以接受可变参数的函数模板和类模板&#xff0c;相比C98/03&#xff0c;类模版和函数模版中只能含固定数量的模版参数&#xff0c;可变模版参数无疑是一个巨大的改进。然而由于可变模版参数比较抽象&#xff0c;使用起来需要一定的技巧&#x…

书面表达优化助手

大家在写文章时肯定被自己的文章写的不够好而烦恼&#xff0c;但是自己又往往看不出问题在哪里&#xff0c;那么有没有一款工具可以根据自己写的文章&#xff0c;给出一个内容优化建议呢&#xff1f; 下面给大家介绍一下书面表达优化助手&#xff0c;它可以根据你的文章给出一个…

已经10岁的K8S,在很多方面仍需努力

/ K8S 已经 10 岁了&#xff0c;但仍然有许多方面要继续努力 / Kubernetes 于 2014 年 6 月推出&#xff0c;自那时起&#xff0c;它在推广云原生应用设计和支持更多微服务部署方面发挥了巨大作用。容器部署的增长非常迅速&#xff0c;而 Kubernetes 对于企业管理这些部署至关重…

FlashAttention解析——大预言模型核心组建

论文名称&#xff1a;FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness 论文地址&#xff1a;https://arxiv.org/abs/2205.14135 一、研究FlashAttention的Motivate FlashAttention技术在现在的主流大语言模型中均有应用&#x…

Java--多态和抽象类

目录 多态实现多态的条件重写重写和重载静态绑定和动态绑定向上转型和向下转型向上转型向下转型instanceof 抽象类 多态 在Java中多态就是在完成一件事的时候&#xff0c;不同人去完成产生的结果不同 比方说打印&#xff0c;我们就是要打印一个东西&#xff0c;如果我们交给彩色…

性能提升20%,字节跳动HTTPDNS从中心下沉到边缘

摘要&#xff1a;本文介绍了HTTPDNS服务从中心迁移至边缘详细的落地过程。主要内容为&#xff1a; HTTPDNS下沉边缘实践遇到的挑战&#xff0c;包括服务放置、流量调度 HTTPDNS下沉边缘解决方案 从性能、成本出发&#xff0c;谈谈HTTPDNS下沉边缘后的收益 传统的DNS流程中…

微信小程序-获取手机号:HttpClientErrorException: 412 Precondition Failed: [no body]

问题&#xff1a; 412 异常就是你的请求参数获取请求头与服务器的不符&#xff0c;缺少请求体&#xff01; 我的问题&#xff1a; 我这里获取微信手机号的时候突然给我报错142&#xff0c;但是代码用的是原来的代码&#xff0c;换了一个框架就噶了&#xff01; 排查问题&am…

java算法day27

java算法day27 动态规划初步总结509 斐波那契数杨辉三角打家劫舍完全平方数 动态规划初步总结 如果你感觉某个问题有很多重叠子问题&#xff0c;使用动态规划是最有效的。 动态规划的过程就是每一个状态一定是由上一个状态推导出来的&#xff0c;这一点就区分于贪心了。贪心是…

鄂维南院士:人工智能的零数据、小数据、大数据和全数据方法

源自&#xff1a; 中国计算机学会 注&#xff1a;若出现无法显示完全的情况&#xff0c;可 V 搜索“人工智能技术与咨询”查看完整文章 人工智能、大数据、多模态大模型、计算机视觉、自然语言处理、数字孪生、深度强化学习 课程也可加V“人工智能技术与咨询”报名参加学习 致…

android java socket server端 可以不断的连接断开,不断的收发 TCP转发

adb.exe forward tcp:5902 tcp:5902 前面本地5901 转发到 后面设备为5902查看转发 adb forward --list删除所有转发 adb forward --remove-allpublic static final String TAG "Communicate";private static boolean isEnable;private final WebConfig webConfig;//…