写在前面

最近听beyond乐队的《灰色轨迹》听上瘾了,300多遍,震惊!!尤其喜欢最后一分半钟的吉他solo,真可谓吉他没有酒,依然让我醉如老狗。。

翻了翻网上的视频,瞬间觉得单身20年的手速都望尘莫及~~

默默拿起尤克里里弹着儿歌《小星星》不禁老泪纵横。。我撅腚要学会首像样的曲子!

学什么好呢?琢磨半天灵光一闪,《士兵突击》中不是有段吉他弹奏的曲子,名字叫《前向きな乙女心》吗?

当初说了“如果学吉他也会是因为这首曲子吧”,想不到一语成谶,那就从它开始吧!!

上网搜关于这首曲的尤克里里四线谱,找不到,淘宝问客服客服说老师还没有录制关于这首的教学视频。。

我就听,这首时长1分42秒,觉得并不是很难,一边听一遍试,发现还是很简单的~~

3 3 4 5 2 1 5 1 3 3 2 1 2 5 3

3 3 4 5 2 1 5 1 3 5 4 3 2 1 1

到30秒后发现自己图样图森破,手速太慢试不出来了。。

怎么办呢?

第一个想到的是搜计算器,会发声的计算器,微博看到有人用5个相同的计算器弹《名侦探柯南》主题曲~太机智了~但是搜了一圈,已经快十点了,好多店铺客服都打卡下班了~

于是想网上有没有模拟音阶的软件呢?有,但是好多都是钢琴的,下载了一个貌似还带病毒,买了佛冷。并且钢琴和尤克里里音色还不大一样,就想能不能通过程序搞一些愉快的事情?

有人写了关于钢琴的程序,果然是一帮有理想的骚年,钢琴88键都能做出来,我这1234567i岂不是小儿科啊,说干就干!

步骤分解

1.一边弹一边用手机录音1234567i

没错,用手机。。音质差了点,不过听了一遍还在能忍受的范围。

2.音频截取

1234567i,8段短音频,每段大概2秒钟的样子,个别音长达4秒,不禁感叹这把琴的延音是真滴好,钱花哪哪值啊(¥2480 经费在燃烧.......)。

3.界面设计

界面两个框,上面一个框实时显示按键按下后的按键数值,下面框保留自己满意的音阶,clear按钮用于快速清除框内值

框下面1234567i按键阵列,先这样吧,丑是丑了点能用先。。

4.撸代码

采用HTML5,根据按键值,读取对应的音频文件

5.哦了

(这里有个问题, 播放按键由于js无法控制匀速, 播放暂时留了个bug,  js如何实现sleep呢?欢迎留言)

置灰部分先忽略吧,搬完家抽时间再搞~

6.发布微信小程序

下一步准备将程序迁移至微信小游戏,之前是默认的“打飞机”,并未发布。这下把这个程序发布一下,手机上操作不是更方便吗?

微信小程序是js写,似乎更简单了~

按键图片

按键对应音频

编程,测试

发布小程序

关键代码

目录结构

index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>ukulele</title>
<link rel="stylesheet" href="css/ukulele.css" />
<script type="text/javascript" src="js/ukulele.js" ></script>
</head>
<body>
<div align="center">
<h1>ukulele弹奏模拟器v1.0</h1>
<table>
<tr>
<td align="right">
<textarea id="temp" class="textarea"></textarea>
</td>
<td align="left">
<input type="button" class="cls" onclick="resetTemp()" />
</td>
</tr>
<tr>
<td align="right">
<textarea id="replay" class="textarea"></textarea>
</td>
<td align="left">
<input type="button" class="cls" onclick="resetPlay()">
<input type="button" class="play" onclick="replay()" />
</td>
</tr>
</table>
</div>
<div align="center">
<h1>Key(1=do 2=re 3=mi 4=fa 5=so 6=la 7=xi 0=i)</h1>
<audio src="mp3/1do.mp3" id="do" controls="controls" hidden="hidden"></audio>
<audio src="mp3/2re.mp3" id="re" controls="controls" hidden="hidden"></audio>
<audio src="mp3/3mi.mp3" id="mi" controls="controls" hidden="hidden"></audio>
<audio src="mp3/4fa.mp3" id="fa" controls="controls" hidden="hidden"></audio>
<audio src="mp3/5so.mp3" id="so" controls="controls" hidden="hidden"></audio>
<audio src="mp3/6la.mp3" id="la" controls="controls" hidden="hidden"></audio>
<audio src="mp3/7xi.mp3" id="xi" controls="controls" hidden="hidden"></audio>
<audio src="mp3/ido.mp3" id="ido" controls="controls" hidden="hidden"></audio>
<input type="button" class="key" id="97" onclick="press()" style="background-image: url(img/1.png);" />
<input type="button" class="key" id="98" onclick="press()" style="background-image: url(img/2.png);" />
<input type="button" class="key" id="99" onclick="press()" style="background-image: url(img/3.png);" />
<input type="button" class="key" id="100" onclick="press()" style="background-image: url(img/4.png);" />
<input type="button" class="key" id="101" onclick="press()" style="background-image: url(img/5.png);" />
<input type="button" class="key" id="102" onclick="press()" style="background-image: url(img/6.png);" />
<input type="button" class="key" id="103" onclick="press()" style="background-image: url(img/7.png);" />
<input type="button" class="key" id="96" onclick="press()" style="background-image: url(img/0.png);" />
</div>
<div align="center">
<input type="button" class="demo" id="christmas" onclick="christmas()" style="background-image: url(img/christmas.png);" alt="圣诞歌"/>
<input type="button" class="demo" id="star" onclick="star()" style="background-image: url(img/star.png);" alt="小星星"/>
<input type="button" class="demo" id="bee" onclick="bee()" style="background-image: url(img/bee.png);" alt="小蜜蜂"/>
</div>
</body>
</html>

ukulele.js

function press(){
var currentId = parseInt(event.currentTarget.id);
ring(currentId);
var yinjie = toYinjie(currentId);
append(yinjie);
} window.onkeydown = function(event){
var keyCode = event.keyCode;
ring(keyCode);
var arr = [97,98,99,100,101,102,103,96,49,50,51,52,53,54,55,48];
var boo = isInArray(arr, keyCode);
if(boo){
var yinjie = toYinjie(keyCode);
append(yinjie);
}
} function ring(value){
var targetId = toTargetId(value);
var element = document.getElementById(targetId);
keyDown(element);
} function keyDown(element){
if(element!=null){
element.currentTime = 0;
if(element.paused){
element.play();
}
}
} function resetTemp(){
document.getElementById("temp").value = "";
} function resetPlay(){
document.getElementById("replay").value = "";
} function append(value){
document.getElementById("temp").value += value;
document.getElementById("replay").value += value;
} function replay(){
var vals = document.getElementById("replay").value;
// 自动演奏 间隔1s
for(var i = 0; i < vals.length; i++){
var v = vals.charAt(i);
var keycode = toKeycode(v);
async function test(){
var temple=await sleep(1000);
ring(parseInt(keycode));
return temple;
}
// ring(parseInt(keycode));
}
} function sleep (time) {
return new Promise((resolve) => setTimeout(resolve, time));
} function isInArray(arr, value){
for(var i = 0; i < arr.length; i++){
if(value === arr[i]){
return true;
}
}
return false;
} function toKeycode(value){
var keycode = null;
switch(value){
case "1":
keycode = "97";
break;
case "2":
keycode = "98";
break;
case "3":
keycode = "99";
break;
case "4":
keycode = "100";
break;
case "5":
keycode = "101";
break;
case "6":
keycode = "102";
break;
case "7":
keycode = "103";
break;
case "i":
keycode = "96";
break;
}
return keycode;
} function toYinjie(value){
var yinjie = null;
switch(value){
case 49:
case 97:
yinjie = "1";
break;
case 50:
case 98:
yinjie = "2";
break;
case 51:
case 99:
yinjie = "3";
break;
case 52:
case 100:
yinjie = "4";
break;
case 53:
case 101:
yinjie = "5";
break;
case 54:
case 102:
yinjie = "6";
break;
case 55:
case 103:
yinjie = "7";
break;
case 48:
case 96:
yinjie = "i";
break;
}
return yinjie;
} function toTargetId(value){
var targetId = null;
switch(value){
case 49:
case 97:
targetId = "do";
break;
case 50:
case 98:
targetId = "re";
break;
case 51:
case 99:
targetId = "mi";
break;
case 52:
case 100:
targetId = "fa";
break;
case 53:
case 101:
targetId = "so";
break;
case 54:
case 102:
targetId = "la";
break;
case 55:
case 103:
targetId = "xi";
break;
case 48:
case 96:
targetId = "ido";
break;
}
return targetId;
} // 圣诞歌
function christmas(){ }
// 小星星
function star(){ }
// 小蜜蜂
function bee(){ }

ukulele.css

h1{
color: blueviolet;
}
.textarea {
width: 650px;
height: 250px;
background: rgba(255, 255, 255, 0);
font-family: "arial, helvetica, sans-serif";
font-size: 48px;
color: purple;
border-color: pink;
}
.key{
width:100px;
height:100px;
background-size: 100% 100%;
border-radius: 50px;
background-color: pink;
outline: none;
}
.cls , .play{
width: 86px;
height: 86px;
border-radius: 5px;
background-color: deepskyblue;
outline: none;
}
.cls{
background-image: url(../img/del.png);
}
.play{
background-image: url(../img/play.png);
}
.demo{
width: 130px;
height: 100px;
border-radius: 20px;
background-color: pink;
outline: none;
}
body {
background-image: url(../img/bg.png);
background-repeat: no-repeat;
background-attachment: fixed;
background-size: cover;
}

代码已分别上传至github,喜欢的园友可以戳戳

简单感受一下~

https://xiguanchendian.github.io/ukulele/

以下是微信小程序识别码,欢迎试玩鸭~

感谢

ukulele弹奏模拟器v1.0(待完善)的更多相关文章

  1. 卡西欧(casio)Fx-5800p程序调试器||模拟器V1.0

    一.[背景] 都说这个卡西欧5800p没有模拟器,模拟器其实就是个电脑上面模拟他的界面的东西,但是真正的核心,他不一定有,而且我们测量方面的编程及时有了所谓的外形模拟器也学不会的. 都说这个卡西欧58 ...

  2. 03-c#入门(简易存款利息计算器v1.0)

    本想把练习题做了的结果放上来,不过发现附录是有答案的,就算了吧,自己做了没问题就行了哈.之前提到过,要是有朋友有想法,需要做小工具我可以帮忙实现,不过貌似大家都很忙.SO,自己学完第4章后,决定做一个 ...

  3. 请各位帮帮忙:Android LBS应用——CityExplorer (v1.0) 调研

    Hello哇各位亲!! 请各位帮帮忙:Android LBS应用——CityExplorer(V1.0)调研 嗯,这个事情是这样的,要填一个调查问卷,但是问卷中部分问题是关于这个叫做CityExplo ...

  4. 【转】寻找最好的笔记软件:三强篇(EverNote、Mybase、Surfulater) (v1.0) (

    原文网址:http://blog.sina.com.cn/s/blog_46dac66f01000b57.html 寻找最好的笔记软件:三强篇(EverNote.Mybase.Surfulater) ...

  5. 《Ruby语言入门教程v1.0》学习笔记-01

    <Ruby语言入门教程v1.0> 编著:张开川 邮箱:kaichuan_zhang@126.com 想要学习ruby是因为公司的自动化测试使用到了ruby语言,但是公司关于ruby只给了一 ...

  6. Omi v1.0震撼发布 - 令人窒息的Web组件化框架

    原文链接--https://github.com/AlloyTeam/omi 写在前面 Omi框架经过几十个版本的迭代,越来越简便易用和强大. 经过周末的连续通宵加班加点,Omi v1.0版本终于问世 ...

  7. Rookey.Frame v1.0极速开发平台稳定版发布

    Rookey.Frame v1.0经过一年时间的修改及沉淀,稳定版终于问世了,此版本经过上线系统验证,各个功能点都经过终端用户验证并持续优化,主要优化以下几个方面: 1.性能较原来提升3倍之多 2.修 ...

  8. 软考论文的六大应对策略V1.0

    软考论文的六大应对策略V1.0 短短2个小时,要写3000字的文章,对习惯了用电脑敲字.办公的IT从业人员而言,难度不小.尤其,大家会提笔忘字.笔者的应试策略,就是勤学苦练,考试前的一个星期,摸清套路 ...

  9. Omi v1.0震撼发布 - 开放现代的Web组件化框架

    原文链接--https://github.com/AlloyTeam/omi 写在前面 Omi框架经过几十个版本的迭代,越来越简便易用和强大. 经过周末的连续通宵加班加点,Omi v1.0版本终于问世 ...

随机推荐

  1. 当ABAP遇见普罗米修斯

    Jerry每次在工作场合中同Prometheus(普罗米修斯)打交道时,都会"出戏",因为这个单词给我的第一印象,并不是用go语言实现的微服务监控利器,而是名导雷德利·斯科特(Ri ...

  2. [LeetCode] 647. 回文子串 ☆☆☆(最长子串、动态规划、中心扩展算法)

    描述 给定一个字符串,你的任务是计算这个字符串中有多少个回文子串. 具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被计为是不同的子串. 示例 1: 输入: "abc" ...

  3. 得到List<HashTable>里面的list然后取list的某一项

    //得到List<HashTable>里面的listUnFix然后取listUnFix判断tempfix里面得值 List<Hashtable> list = new List ...

  4. Upgrade Windows Server 2016 to Windows Server 2019

    Pre-Upgrade Upgrade path: Windows Server 2016 can be upgraded to Windows 2019 in a single upgrade pr ...

  5. Linux 目录和文件的操作

    整理常用的linux命令,关于目录和文件的操作,用于巩固记忆,以备不时之需. [root@localhost ~] root:当前用户 localhost:主机名 ~:当前所在位置 符号#:管理员 符 ...

  6. nginx的stream模块和upstream模块

    nginx7层调度方式 使用upstream模块定义集群名称和节点地址 定义在server字段之外httpd字段之内 upstream staticweb { server 172.17.0.2; # ...

  7. MSF MS11-050攻击实践及内存保护技术

    内存攻击指的是攻击者利用软件安全漏洞,构造恶意输入导致软件在处理输入数据时出现非预期错误,将输入数据写入内存中的某些特定敏感位置,从而劫持软件控制流,转而执行外部输入的指令代码,造成目标系统被获取远程 ...

  8. HTML常用全部代码--第一部分--HTML/CSS( 小伙伴要牢记😁😁😁😁 )

    <一>html代码大全:结构性定义 (1) 文件类型<HTML></HTML> (放在档案的开头与结尾) (2) 文件主题<TITLE></TIT ...

  9. Echo团队Alpha冲刺 - 测试随笔

    目录 测试工作的安排 测试工具选择和运用 测试用例文档 测试体会 项目测试评述 测试工作的安排 模块 测试人 测试内容 单元测试 李东权,黄少勇 测试类或者函数是否能正确处理用户请求 接口测试 林弘杰 ...

  10. MySQL命令操作(Linux平台)

    Linux shell 批量创建数据库/表 Shell 脚本如下: # create database and table HOST='localhost' PORT='3306' USER='roo ...