Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
61
Help us understand the problem. What is going on with this article?
@PianoScoreJP

JavaScriptでMIDIファイルを解析してみる 1

More than 5 years have passed since last update.

MIDIの構造について

ここの解説をもとに解析してみます。
ちなみにMIDIファイルのことをSMF(Standard MIDI FILE)と呼びます。
なので以降SMFと呼びます。

使用する環境

Node.js

SMFを16進数データとして読み込む

解析するために16進数並びのテキストデータとして読み込みます。
こんな感じです。

loadsmf.js

/**
*midiファイルを読み込んで16進数テキストを返します。
*@param {string} filepath 読み込むMIDIファイルのパス
*@callback コールバック
*@return {string} 16進数テキスト
**/
var fs=require("fs");

module.exports=function(filepath,callback){
    fs.readFile(filepath,function(err,data){
      if(err) throw err;
      //読み込んだデータを16進数テキストに変換(小文字は大文字に変換)
      var hexString = Buffer(data).toString("hex").toUpperCase();
      callback(hexString);
    });
}

SMFのヘッダ

SMFのヘッダはヘッダチャンクと呼ばれているようです。
構造は下記のようになっています。

1.チャンクタイプ(4byte)

これは4D 54 68 64という値で固定です。まずこれで正しいSMFかどうか判定できますね。

2.データ長(4byte)

ヘッダのサイズは可変長になっているようです。
ですのでここでヘッダサイズを保持しています。
ここでいうヘッダサイズは、この後に続くヘッダデータのデータ長です。
将来の拡張用で保持してあるだけで、実際はほとんど6バイトなのかな?
データ例:00 06

3.フォーマット(2byte)

SMFには0,1,2いずれかのフォーマットタイプがあるようです。
ですので、フォーマットのデータは00 0000 0100 02になります。
フォーマットの詳細については、記事最初にあるリンク等で確認してください。

4.トラック数(2type)

トラックの数を示します。
トラックが1つなら 00 01となります。

5.時間単位(2byte)

分解能のこと。
SMFでは時間管理の方法について、「何小節何拍」と「何分何秒何フレーム」のいずれか指定をすることができます。
でもだいたいは「何小節何拍」のほうを使うようです。
これは2バイトあるうちの上位バイトの最上位ビットが0か1か、で決定されます。
つまり上位バイトが
00~79(最上位ビットが0)なら、「何小節何拍」
80~FF(最上位ビットが1)なら、「何分何秒何フレーム」
となります。

データの例としては
01 E0で、分解能が480 何小節何拍で指定する
となります。

ちなみに、通常使われるは分解能の値は、480です。

これは4分音符が、480という数値で表されると考えてください。
ただしこれは秒とか、ミリ秒とかを表すような単位ではありません。

あくまで、4分音符を480と表現するだけです。
4分音符を480としておけば、8分音符は240、2分音符は960と表現されます。
SMFでは、「4分音符」とか「8分音符」とかいう形での「音価」の表現はない代わりに、4分音符を480としておくことで、何音符か、を表現しているのです。

また、実際の演奏時間は、テンポを保持しているデータから計算しますが、詳細は別記事で解説します。

以上がヘッダチャンクの概要です

とりあえずここまでを取得するテストスクリプトを作ってみますと

    //dataは16進テキストにしたsmfファイルの中身
    var  header ={
        //チャンクタイプ
        chunktype:data.substring(0,8),
        //ヘッダサイズ
        headersize : data.substring(9,16),
        //SMFフォーマット
        format : data.substring(16,20),
        //トラックの数
        tracksize : data.substring(20,24),
        //時間単位
        timeunit : data.substring(24,28),
    }
    console.dir(header);
}

単純に文字列関数で、何文字目にあるか、で取得してみました。
本来ヘッダは可変長なので、headersizeで読み込むサイズを決める必要があるのですが、
ここでは割愛。6バイト前提です。

さて、これで単純なSMFを読み込んでみたいと思います。
テストで使用するのは、MuseScoreというフリーの楽譜作成ソフトで作成したピアノ楽譜をMIDIエクスポートしたものです。ピアノ楽譜です。トラック数は右手と左手で2つになるようです。
キャプチャ.PNG

C5の四分音符を鳴らすだけです。

このSMFを上記スクリプトで読み込んでみると

{ chunktype: '4D546864',
  size: '0000006',
  format: '0001',
  tracksize: '0002',
  timeunit: '01E0'
}

こんなデータが返ってきました。
ヘッダ、ヘッダサイズ、ヘッダの各情報を取得することができましたね。

音の情報はこの後に続きますが考え方は同じです。
詳しくはJavaScriptでMIDIファイルを解析してみる 2で解説します。

61
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
61
Help us understand the problem. What is going on with this article?