吉他初学者学习网站搭建系列(8)——如何练习音阶

文章目录

  • 背景
  • 实现
    • 吉他面板
    • 音阶位置
    • 音阶识别
  • 结语

背景

大家好,我是一个爱好音乐的非典型程序员!我最近又往自己的网站中集成了一个模块——音阶。下面介绍一下背景。

在这里插入图片描述

很多吉他初学者在掌握了一些音阶知识后,可能不知道怎么训练自己的对音阶的敏感度,以及如何在指板中把某个调的某个音阶给爬熟练了。

首先,要掌握指板上的所有音,推当然很简单,但是要做到立即反应,恐怕一开始还有些困难,如果这个时候能直观看到某个音阶在指板中的所有位置,那对于训练是很有帮助的。

其次,如果每弹一个音都能看出是否弹对,在一个有反馈的环境中练习,对音阶的练习就更是有利。

那么,怎么实现这样一个工具呢?

我分为三部分实现:吉他面板功能、音阶位置、音阶识别。

实现

吉他面板

在这里插入图片描述
首先,我们知道,标准调音的吉他,六弦到一弦的音分别是:E2、A2、D3、G3、B3、E4。然后,每一品,都相隔一个半音。那么有了这些空弦的标准音的音名,基于C、D、E、F、G、A、B、C这样一个八度的音名排列,以及全、全、半、全、全、全、半的音程关系,我们就可以推算出所有品格的具体音名了!

举个例子,例如从二弦空弦开始往右,B+一个半音=C,C+一个半音=C#,以此类推。只有B和E到下一个音是半音,其余都是两个半音。

另外,如果把音高都考虑进去,我们需要在B>C时,将后面的数字+1。例如:B3+一个半音=C4。因为标准情况下,C是do。

那么很容易就得到一个推后一个半音的方法:

/*** 获取下一个半音* @param {*} note * @returns */
export function nextRule(note) {const key = note.slice(0, -1);const num = note.slice(-1);let nextKey = '', nextNum = num;if (key.length === 1) {if (['b', 'e'].includes(key)) {// 半音nextKey = seq[(seq.indexOf(key) + 1) % 7];} else {// 全音nextKey = `${key}#`}if (key === 'b') {nextNum = Number(num) + 1;}} else {nextKey = key.slice(0, 1); // end with bif (key.endsWith('#')) {nextKey = seq[(seq.indexOf(nextKey) + 1) % 7];}}return `${nextKey}${nextNum}`
}

这样,20个品的音名就都得出来了。

音阶位置

音阶的种类很多,常用的列表大致如下:

// 音阶列表
export const ScaleList =['major','minor','ionian', // (Alias for major)'dorian','phrygian','lydian','mixolydian','aeolian', // (Alias for minor)'locrian','majorpentatonic','minorpentatonic','chromatic','harmonicchromatic', // (Alias for chromatic)'blues','doubleharmonic','flamenco','harmonicminor','melodicminor','wholetone',]

利用teoria库,我们可以根据根音和音阶,获得该音阶的所有构成音的音名,

generateScale(note, scale) {const n = Note(note); // 根音const s = Scale(n, scale); // 获取音阶this.strings = s.notes().map(t => t.toString()); // 例如,a大调构成音为["a3","b3","c#4","d4","e4","f#4","g#4"]

这里我们做些处理,把最后一位数字去除。这样就得到:

let string = ["a","b","c#","d","e","f#","g#"]

有了这些构成音,再结合吉他上指板的音名,我们可以知道哪个位置命中了这个音阶的构成音。

/*** 默认规则:0品灰色,不含升降调的绿色* @returns */
export function guitarStyle(curNote, strings = []) {let c = ''const root = strings[0];const match = strings.find(note => {return (note.slice(0, -1) === curNote.slice(0, -1) || getSameNote(note)?.slice(0, -1) === curNote.slice(0, -1))})if (match) {c = 'normal'; // 音阶构成音}if (curNote.slice(0, -1) === root.slice(0, -1)) {c = 'root'; // 根音}return c;
}

最后就渲染成上面图片中的音阶分布图了!

音阶识别

最后一步,我们需要利用AudioContext,获取吉他的音频。原理和吉他初学者学习网站搭建系列(3)——如何实现吉他在线调音相同,核心是拿到频率后,利用teoria.note.fromFrequency将频率转化为音,有了音名,再根据吉他指板的音名和索引号缓存,找到对应索引,然后直接渲染那一格的颜色即可!

代码如下:

 import { note } from 'teoria';detectPitch(analyserNode, detector, input, sampleRate) {analyserNode.getFloatTimeDomainData(input);const [pitch, clarity] = detector.findPitch(input, sampleRate);this.pitch = Math.round(pitch * 10) / 10;this.clarity = Math.round(clarity * 100);try {if (this.clarity > 95) {const n = note.fromFrequency(this.pitch);this.currentNote = n.note.toString();}} catch (err) { }requestAnimationFrame(() => this.detectPitch(analyserNode, detector, input, sampleRate));}

由于相同音高在不同弦上不同位置可以一致,因此每次弹奏都会有几个格同时识别出,这是正常的。此外,由于频率时刻在变化,因此识别是会有一定的延迟和偏差,最好是调好吉他的音,并且弹奏有力,这样更容易识别出来!

结语

最终,我们可以边弹奏音阶,边在网站中看到自己弹的是否正确,并且学会跟弹音阶,形成肌肉记忆。快来用起来吧!!加油,各位😄💪

网站地址:https://tryiscool.space/music-score/#/scale
B站账号:玉儿或芋头

欢迎关注,交个朋友,我还在持续学习乐理!相信对你有帮助。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

15.三数之和 python

三数之和 题目题目描述示例 1:示例 2:示例 3:题目链接 题解Python 实现解释提交结果 题目 题目描述 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k ,同时还满…

tauri使用github action打包编译多个平台arm架构和inter架构包踩坑记录

这些error的坑,肯定是很多人不想看到的,我的开源软件PakePlus是使用tauri开发的,PakePlus是一个界面化将任何网站打包为轻量级跨平台软件的程序,利用Tauri轻松构建轻量级多端桌面应用和多端手机应用,为了实现发布的时候…

Redis 中的 hcan 命令耗内存,有什么优化的方式吗 ?

Redis 中的 hcan 命令耗内存,有什么优化的方式吗 ? 1. 使用合适的游标值:2. 控制每次迭代返回的键数量:3. 避免长时间运行的迭代:4. 使用HSCAN与SCAN命令结合:5. 优化哈希表结构:6. 监控和调整R…

Android 12.0 DocumentsUI文件管理器首次进入默认显示内部存储文件功能实现

1.前言 在12.0的系统rom定制化开发中,在关于文件管理器的某些功能中,在首次进入文件管理器的时候默认进入下载 文件夹,点击菜单选择内部存储的时候,会显示内部存储的内容,客户开发需要要求默认显示内部存储的文件 接下来分析下功能的实现 如图: 2.DocumentsUI文件管理器首…

抓包之wireshark基础用法介绍

写在前面 wireshark作为最优秀的抓包工具,有必要详细的看下其基本用法,所以本文就一起来做这件事吧! 1:初步介绍 打开wireshark首先会进入如下的界面: 想要开始抓包,需要进行如下操作: 接着…

【GPT】力量训练是什么,必要吗,有可以替代的方式吗

什么是力量训练? 力量训练是一种通过抵抗力(如重量、阻力带、自身体重等)来刺激肌肉收缩,从而提高肌肉力量、耐力和体积的运动形式。它包括以下常见形式: 自由重量训练:使用哑铃、杠铃、壶铃等。固定器械…

【接口调试】OpenAI ChatGPT API

【接口调试】AbortController 发出请求finish_reason 参数细节 – Openai ChatGPT 文档 发出请求 可以将以下命令粘贴到终端中以运行第一个API请求。 请确保用您的秘密API密钥替换$OPENAI_API_KEY。 curl https://api.openai.com/v1/chat/completions \-H "Content-Ty…

【Java基础入门篇】二、控制语句和递归算法

Java基础入门篇 二、控制语句和递归算法 2.1 switch-case多分支选择语句 switch执行case语句块时,若没有遇到break,则运行下一个case直到遇到break,最后的default表示当没有case与之匹配时,默认执行的内容,代码示例如…

【人工智能学习之STGCN训练自己的数据集】

STGCN训练自己的数据集 准备事项数据集制作视频转jsonjsons转jsonjson转npy&pkl 训练STGCN添加图结构修改训练参数开始训练测试 准备事项 st-gcn代码下载与环境配置 git clone https://github.com/yysijie/st-gcn.git cd st-gcn pip install -r requirements.txt cd torc…

Dify+Docker

1. 获取代码 直接下载 (1)访问 langgenius/dify: Dify is an open-source LLM app development platform. Difys intuitive interface combines AI workflow, RAG pipeline, agent capabilities, model management, observability features and more, …

Python 网络爬虫高级教程:分布式爬取与大规模数据处理

经过基础爬虫和进阶爬虫的学习,我们已经掌握了爬虫的基本原理、动态内容处理及反爬机制的应对。然而,当我们面对海量数据或需要高效爬取多个站点时,分布式爬虫和数据存储、处理能力就显得尤为重要。本篇博客将带你迈向网络爬虫的高级阶段&…

Loadsh源码分析-every,some,size,includes

collection相关的函数, collection指的是一组用于处理集合(如数组或对象)的工具函数。 lodash源码研读之every,some,size,includes 一、源码地址 GitHub 地址: GitHub - lodash/lodash: A modern JavaScript utility library delivering mo…

力扣81:搜索旋转排序数组II

已知存在一个按非降序排列的整数数组 nums &#xff0c;数组中的值不必互不相同。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff08;0 < k < nums.length&#xff09;上进行了 旋转 &#xff0c;使数组变为 [nums[k], nums[k1], ..., nums[n-1], n…

性能监控系统Prometheus、Node-exporter与Grafana部署详解搭建

简介 Prometheus、node-exporter整合到Grafana三者结合,构建了一个强大的监控体系,专门用于 Linux 主机和容器的监控。这个体系能够实时收集、分析和可视化各种系统指标。Prometheus是这个监控体系的核心,负责收集和存储来自各个目标的指标数据Node Exporter是一个部署在被监…

数据并行、模型并行与张量并行:深度学习中的并行计算策略(中英双语)

中文版 数据并行、模型并行与张量并行&#xff1a;深度学习中的并行计算策略 随着深度学习模型的不断增大&#xff0c;单个计算节点&#xff08;例如单个 GPU&#xff09;的计算和内存能力逐渐成为了限制训练效率和模型规模的瓶颈。为了应对这些挑战&#xff0c;深度学习社区…

Android so库的编译

在没弄明白so库编译的关系前,直接看网上博主的博文,常常会觉得云里雾里的,为什么一会儿通过Android工程cmake编译,一会儿又通过NDK命令去编译。两者编译的so库有什么区别? android版第三方库编译总体思路: 对于新手小白来说搞明白上面的总体思路图很有必…

Java函数式编程+Lambda表达式

文章目录 函数式编程介绍纯函数Lambda表达式基础Lambda的引入传统方法1. 顶层类2. 内部类3. 匿名类 Lambda 函数式接口&#xff08;Functional Interface&#xff09;1. **函数式接口的定义**示例&#xff1a; 2. **函数式接口与Lambda表达式的关系**关联逻辑&#xff1a;示例&…

Linux操作系统2-进程控制3(进程替换,exec相关函数和系统调用)

上篇文章&#xff1a;Linux操作系统2-进程控制2(进程等待&#xff0c;waitpid系统调用&#xff0c;阻塞与非阻塞等待)-CSDN博客 本篇代码Gitee仓库&#xff1a;Linux操作系统-进程的程序替换学习 d0f7bb4 橘子真甜/linux学习 - Gitee.com 本篇重点&#xff1a;进程替换 目录 …

文件上传漏洞:你的网站安全吗?

文章目录 文件上传漏洞攻击方式&#xff1a;0x01绕过前端限制0x02黑名单绕过1.特殊解析后缀绕过2..htaccess解析绕过3.大小写绕过4.点绕过5.空格绕过6.::$DATA绕过7.配合中间件解析漏洞8.双后缀名绕过9.短标签绕过 0x03白名单绕过1.MIME绕过(Content-Type绕过)2.%00截断3.0x00截…

设计模式-适配器模式-注册器模式

设计模式-适配器模式-注册器模式 适配器模式 如果开发一个搜索中台&#xff0c;需要适配或接入不同的数据源&#xff0c;可能提供的方法参数和平台调用的方法参数不一致&#xff0c;可以使用适配器模式 适配器模式通过封装对象将复杂的转换过程隐藏于幕后。 被封装的对象甚至…