用JS生成MIDI文件(附代码)

Posted LiemZuvon

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用JS生成MIDI文件(附代码)相关的知识,希望对你有一定的参考价值。

用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文件(附代码)的主要内容,如果未能解决你的问题,请参考以下文章

用JS生成MIDI文件(附代码)

用JS生成MIDI文件(附代码)

如何将从音轨 1 提取的速度应用到 MIDI 文件的其他音轨?

请问怎么在Guitar Pro 5.2里把两个音轨的内容叠加进第三个音轨?

如何在音轨中的特定点触发事件或回调?

BLE MIDIMIDI 文件格式分析总结 ★★★