用JS生成MIDI文件

大家好,近期我们的课程需要我们做一个应用,我的的小组打算做一个在线的伴奏制作平台,这意味着我们需要知道如何在线制作音乐以及如何用浏览器播放。
小编我这一节就跟大家讲解下如何用Javascript从无到有编写一个属于自己的MIDI制作程序吧。

首先,先给大家一篇讲解MIDI格式讲解的链接,中文!小编这里就不多赘述啦。
http://blog.sina.com.cn/s/blog_6f72ff900101f95b.html

首先,我们的一个理解就是MIDI就是由大量的音轨串联而成,然后每个音轨由一个头以及一堆事件构成(作为一个快速上手的例子,小编这里不介绍更多复杂的比如说控制器等东西,只讲音符,感兴趣的读者可以阅读上面的那个链接,都是一样做的~)。就是酱紫!

文件头

我们知道MIDI的文件头就是“MThd”+一串设置(比如说你有多少个音轨啊,播放速度怎么样啊等等)。所以呢,我们就可以写出下面的代码


function init_header(Config) {var rt = "MThd";rt += insertInt(Config.road_length, 4);rt += insertInt(1, 2);rt += insertInt(Config.road_num, 2);rt += insertInt(Config.ticknum, 2);return rt;
}

观察这段代码,你会发现我用了string来存储这些信息,之所以用string呢是因为string足够我们使用了,读者如果觉得还是用字节存储比较好的话推荐学习js的Buffer或Uint8Arrary。
insertInt是一个插入数据的辅助函数,比如说road_length等于20,但是我希望他占据4个字节,那我调用insertInt(road_length, 4)就可以了。可以看到,这个头部是非常的简单的,其实头部还有很多其他的信息可以加,感兴趣的读者可以自行添加,反正就是rt+=的事。至于内容要怎么设定嘛,读者看下那篇博客吧~

音轨

正如一开始介绍的,文件头之后,跟着的是多个串联的音轨。既然是串联,那就只写一个音轨的创建就好啦,其他的一样调用。

音轨头

音轨首先会有音轨头,之后还有事件等等。这里先说音轨头…还是不说了,文件头也是头,音轨头也是头,两个东西的实现几乎没什么两样。下面放代码

function init_road(Config) {var rt = "";rt += "MTrk";rt += insertInt(Config.road_length+15, 4);rt += create_event(0, set_beat(0x4, 0x4));rt += create_event(0, set_speed(0x07A120));return rt;
}

当然,小编说的没什么两样,并不是说内容也是一样,音轨毕竟音轨,跟文件肯定是有本质的区别,这个本质的区别就在上面rt要拼的内容不一样而已…

事件

接下来是事件,事件大体上可以分为音符、控制器和系统信息这几个种类。那么比如一个音符,就会有按下和松开两种情况,当然每个事件都有事件的发生事件间隔(注意是间隔),因此,为了谱一段音乐,我们可以有如下代码。


function create_music(arr, road_num, len, Config) {var rt = "";var time = 0;for (var c = 0; c < len; c++) {time += Math.floor(Config.ticknum/4);for (key in arr) {if (typeof(arr[key][c]) == 'undefined') continue;if (arr[key][c] == 0) rt += create_event(time, close_note(road_num, key));else rt+=create_event(time, create_note(road_num, key, arr[key][c]));time = 0;}}return rt;
}

其中留意到我们有一个if语句,就是用来判断这个音符是按下还是松开。
然后呢,我们把上面的这些东西拼起来,就可以制作我们自己的midi音乐啦!!

JS代码


function insertZeros(num) {var rt = "";while (num-- > 0) rt+=String.fromCharCode(0);return rt;
}function insertInt(num, room) {var rt = "";while (num > 0) {rt = String.fromCharCode(num%256)+rt;num = Math.floor(num/256);}rt = insertZeros(room-rt.length)+rt;return rt;
}function init_header(Config) {var rt = "MThd";rt += insertInt(Config.road_length, 4);rt += insertInt(1, 2);rt += insertInt(Config.road_num, 2);rt += insertInt(Config.ticknum, 2);return rt;
}function init_road(Config) {var rt = "";rt += "MTrk";rt += insertInt(Config.road_length+15, 4);rt += create_event(0, set_beat(0x4, 0x4));rt += create_event(0, set_speed(0x07A120));return rt;
}function end_road(time) {var rt = "";rt += create_event(time, insertInt(0xff2f, 2));rt += insertZeros(1);return rt;
}function create_event(time, eve) {var rt = "";rt += insertInt(time, 1);rt += eve;return rt;
}function create_music(arr, road_num, len, Config) {var rt = "";var time = 0;for (var c = 0; c < len; c++) {time += Math.floor(Config.ticknum/4);for (key in arr) {if (typeof(arr[key][c]) == 'undefined') continue;if (arr[key][c] == 0) rt += create_event(time, close_note(road_num, key));else rt+=create_event(time, create_note(road_num, key, arr[key][c]));time = 0;}}return rt;
}/*###########EVENT###################*/function change_instru(road, instru) {var rt = String.fromCharCode(0xC0+road);rt += String.fromCharCode(instru);return rt;
}function create_note(road, note, strength) {var rt = "";rt += String.fromCharCode(0x90+road);rt += String.fromCharCode(note);rt += String.fromCharCode(strength);return rt;
}function close_note(road, note) {var rt = "";rt += String.fromCharCode(0x80+road);rt += String.fromCharCode(note);rt += String.fromCharCode(0);return rt;
}function set_beat(fenzi, fenmu) {var rt = "";rt += insertInt(0xff5804, 3);rt += String.fromCharCode(fenzi);rt += String.fromCharCode(Math.floor(Math.log(fenmu)/Math.log(2)));rt += insertZeros(2);return rt;
}function set_speed(speed) {var rt = "";var spd = insertInt(speed, 1);rt += insertInt(0xff51)+String.fromCharCode(spd.length);rt += spd;return rt;
}/*##############################*/
function printBytes(str) {var bytes = [];for (var i = 0; i < str.length; ++i) {bytes.push(str.charCodeAt(i).toString(16));}alert(bytes);
}/*
function writeMidIE(str)
{var fso, tf;fso = new ActiveXObject("Scripting.FileSystemObject");//获取对象tf = fso.CreateTextFile("./test.mid", true);//创建一个文件var bytes = [];for (var i = 0; i < str.length; ++i) {bytes.push(str.charCodeAt(i).toString(16));}tf.Write (bytes);tf.Close();//关闭
}
*/function clickDownload(str)
{  var link = document.createElement('a');link.download = "test.mid";str =  encodeURIComponent(str);  link.href = "data:text/csv;charset=gbk,"+str;  link.click();
}function exec() {var head_config = {road_length : 6, road_num : 1, ticknum : 120};var midi = init_header(head_config);var p = 0x55;var road = 0;var len = 7;var arr = {60:[],62:[ , , , ,p,p,0],64:[ ,p,p,0, , , ],65:[ , , ,p,0, , ],67:[p,0, , , , , ]};var tmp = create_music(arr, road, len, head_config);tmp += end_road(5);var road_config_1 = {road_length : tmp.length};midi+=init_road(road_config_1);midi+=tmp;return midi;clickDownload(midi);//printBytes(midi);
}

其中arr就是乐谱啦,小编只写了533422,读者感兴趣的可以继续完善哦(反正又不难对吧)~毕竟这个是初步代码,小编之后会把完善好的代码放出来。
代码使用方法,打开浏览器开发者工具,然后把上面的代码扔进去,执行exec,下载的文件就可以直接用windows的音乐播放器播放啦~:)
上面代码有啥不懂有啥问题,欢迎留言~

用JS生成MIDI文件(附代码)相关推荐

  1. node.js生成Excel文件 使用node-xlsx

    node.js生成Excel文件 简介 尝试使用excel-export以及excel-export-next没有成功,转为使用node-xlsx成功生成Excel 从接到需求开始查询可用的库 好多博 ...

  2. 启动(程序还没执行生成dump文件的代码)就崩溃的处理流程

    何志丹 如果有专业人员,利用Windbg启动这个程序,在分析就可以了. 上次崩溃,让大家手忙脚乱.所以列了一个启动(程序还没执行生成dump文件的代码)就崩溃的处理流程. 1,用我写的opendll. ...

  3. [技巧]用js生成日志文件、获取ip。

    需求:通过js生成日志文件. 1.npm初始化并载入模块 项目根目录初始化npm npm init -y 下载express模块 npm i express 2.引入express搭建基础功能 使用e ...

  4. windows 平台 js 生成 CSV文件中文乱码解决方案

    惯例:demo 地址 解决方法demo JS生成CSV文件 window 平台下会有中文乱码的问题,原因是windows 平台UTF-8文件头习惯加字节序标记0xEF 0xBB 0xBF,本身UTF- ...

  5. 原生JS生成PDF文件、生成pdf功能

    js实现生成pdf文件 这里我主要做个记录,之前写的现在忘得差不多了,所以直接上代码 先来HTML的代码,这里因为我用的HkCms框架所以{hkcms:adv name="tctotal&q ...

  6. linux下用js生成xml文件,使用JS读取XML文件的方法

    由于项目上需要解析xml,于是各种百度,然后自己总结了下各个主流浏览器解析xml的方法,只能是很浅显的知道他的用法,但是还没有深层次的研究. 不同的浏览器对xml的解析方式不同,根据目前主流浏览器大致 ...

  7. android 解析midi文件,python 解析 MIDI 文件并生成 MIDI 文件

    以下程序将 midi 文件解析出来, 并利用解析出来的音符重新生成一个一样的 midi 文件. import mido import sys import json def midifile_to_d ...

  8. matlab 如何生成mif,用matlab生成mif文件(示例代码)

    a=0:255miffile('rom_ipp.mif',a,8,256) 这是在matlab中调用的miffile函数来生成mif文件 ''单引号里面为所生成文件的名字 注:所生成的文件在matla ...

  9. Keil如何生成bin文件 - 附详细操作图文

    Keil如何生成bin文件 第一步:生成axf输入文件 点击魔术棒"Options for Target" → 单击" Output " → 按下面截图所示配置 ...

最新文章

  1. 如何挖掘医疗数据?看这份KDD2021《异构医疗数据挖掘》教程
  2. RDS for MySQL 5.7 备份恢复为本地实例
  3. 聊聊EIGRP的自动汇总与手工汇总
  4. 数据库分库分表和带来的唯一ID、分页查询问题的解决
  5. .Net面试题(1)
  6. java wordcount程序_WordCount程序(java)
  7. Akka并发编程——第六节:Actor模型(五)
  8. 阿里云天池 Python训练营Task4: Python数据分析:从0完成一个数据分析实战 学习笔记
  9. 基于×××环境下的远程视频监控传输
  10. myeclipse注册机,自己生成注册码
  11. MATLAB遗传算法工具箱的函数及实例
  12. 企业微信可以批量删除聊天记录吗?
  13. Win10清理C盘垃圾
  14. 判断43是不是质数用c语言,1是素数吗(c语言判断一个数为素数)
  15. OPNsense用户手册-用户界面
  16. dx12的resource barrier
  17. HTML5定稿了 为什么原生App世界将被颠覆
  18. StarUML使用说明—用例图、时序图、活动图
  19. golang_微信头像过期失效
  20. Jenkins系列:5、wsl下的Jenkins编译Windows下的Qt程序并自动打包成exe

热门文章

  1. 如何理解CPU密集型 和 I/O密集型
  2. 俄罗斯进口食品还能买吗?
  3. [转帖]一个浙江人的23条经典经商法则
  4. layui+PHP文件上传
  5. 拓嘉辰丰:拼多多开店半个月都没有起色、没有销量怎么办
  6. 河北工业大学计算机专业好就业吗,河北工业大学就业率及就业情况怎么样(含就业前景好的专业)...
  7. CD系列:应用芯片部分总结对应功能
  8. JAVA中String与Date类型互转
  9. HTTP 的 GET/POST区别 及 cookie 和 session 区别
  10. 大一计算机专业学生,该如何自学数据结构和算法