MIDIシーケンスを解析してみた

by nemu90kWw
♥17 | Line 289 | Modified 2010-01-08 16:42:13 | MIT License
play

ActionScript3 source code

/**
 * Copyright nemu90kWw ( http://wonderfl.net/user/nemu90kWw )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/1wsl
 */

package
{
	import flash.display.*;
	import flash.events.*;
	import flash.text.*;
	import flash.net.*;
	import flash.utils.ByteArray;
	import mx.utils.Base64Decoder;
	
	[SWF(width="465", height="465", frameRate="24")]
	public class test extends Sprite
	{
		private var button:Sprite = new Sprite();
		private var buttontext:TextField = new TextField();
		private var fileReference:FileReference;
		
		private var textfield:TextField = new TextField();
		
		private var midi:SMFSequence;
		
		function test()
		{
			textfield.width = 465
			textfield.height = 465-30;
			textfield.y = 30;
			textfield.wordWrap = true;
			addChild(textfield);
			
			var decoder:Base64Decoder = new Base64Decoder();
			decoder.decode(SMFBinary.data);
			
			var ba:ByteArray = decoder.toByteArray();
			ba.uncompress();
			
			midi = new SMFSequence(ba);
			textfield.text = midi.toString();
			
			button.x = 5;
			button.y = 5;
			button.mouseChildren = false;
			button.buttonMode = true;
			button.graphics.lineStyle(1, 0xBBBBBB);
			button.graphics.beginFill(0xEEEEEE);
			button.graphics.drawRoundRect(0, 0, 100, 20, 5, 5);
			button.graphics.endFill();
			addChild(button);
			
			buttontext = new TextField();
			buttontext.width = 100;
			buttontext.height = 20;
			buttontext.htmlText = "<p align='center'><font face='_sans'>開く</span></p>";
			button.addChild(buttontext);
			
			fileReference = new FileReference();
			fileReference.addEventListener(Event.SELECT, onSelect);
			fileReference.addEventListener(Event.COMPLETE, onComplete);
			button.addEventListener(MouseEvent.CLICK, onClick);
		}
		
		private function onClick(event:MouseEvent):void 
		{
			fileReference.browse([new FileFilter("MIDIシーケンス(mid)", "*.mid")]);
		}
		
		private function onSelect(event:Event):void
		{
			fileReference.load();
		}
		
		private function onComplete(event:Event):void
		{
			midi = new SMFSequence(fileReference.data);
			textfield.text = midi.toString();
		}
	}
}

	import flash.utils.ByteArray;
	
	class SMFSequence
	{
		public var format:int;
		public var numTracks:int;
		public var division:int;
		public var tempo:int = 0;
		public var title:String = "";
		public var artist:String = "";
		public var signature_n:int;
		public var signature_d:int;
		public var length:int;
		
		public var tracks:Vector.<SMFTrack> = new Vector.<SMFTrack>();
		
		function SMFSequence(bytes:ByteArray)
		{
			bytes.position = 0;
			
			while(bytes.bytesAvailable > 0)
			{
				var type:String = bytes.readMultiByte(4, "us-ascii");
				switch(type)
				{
				case "MThd":	//ヘッダ
					bytes.position += 4;	//ヘッダのデータ長は常に00 00 00 06なのでスルー
					format = bytes.readUnsignedShort();
					numTracks = bytes.readUnsignedShort();
					division = bytes.readUnsignedShort();
					break;
				case "MTrk":	//トラック
					var len:uint = bytes.readUnsignedInt();
					var temp:ByteArray = new ByteArray();
					bytes.readBytes(temp, 0, len);
					var track:SMFTrack = new SMFTrack(this, temp);
					tracks.push(track);
					length = Math.max(length, track.length);
					break;
				default:
					return;
				}
			}
		}
		
		public function toString():String
		{
			var text:String = "format : "+format+" | numTracks : "+numTracks+" | division : "+division+"\n";
			text += "タイトル : "+title+" | 著作権表示 : "+artist+"\n";
			text += "拍子 : "+signature_d+"分の"+signature_n+"拍子 | BPM : "+tempo+" | length : "+length+"\n";
			
			text += "\n";
			
			for(var i:int = 0; i < tracks.length; i++)
			{
				text += "トラック"+i+" : "+tracks[i].toString() + "\n";
			}
			
			return text;
		}
	}

	class SMFTrack
	{
		public var parent:SMFSequence;
		public var sequence:Vector.<SMFEvent> = new Vector.<SMFEvent>();
		public var length:int;
		
		function SMFTrack(parent:SMFSequence, bytes:ByteArray)
		{
			this.parent = parent;
			
			var event:SMFEvent;
			var temp:int;
			var len:int;
			
			var type:int;
			var channel:int;
			
			var time:int;
			/*
			var readVariableLength:Function = function(time:uint = 0):uint
			{
				var temp:uint = bytes.readUnsignedByte();
				if(temp & 0x80) {return readVariableLength(time + (temp & 0x7F));}
				else {return time + (temp & 0x7F);}
			}
			*/
			var readVariableLength:Function = function(time:uint = 0):uint
			{
				var temp:uint = bytes.readUnsignedByte();
				if(temp & 0x80) {return readVariableLength((time << 7) + (temp & 0x7F));}
				else {return (time << 7) + (temp & 0x7F);}
			}
			
			main : while(bytes.bytesAvailable > 0)
			{
				event = new SMFEvent();
				event.delta_time = readVariableLength();
				time += event.delta_time;
				event.time = time;
				
				temp = bytes.readUnsignedByte();
				
				if(temp == 0xFF)
				{
					event.type = bytes.readUnsignedByte();
					len = readVariableLength();
					
					switch(event.type)
					{
					case 0x02:	//作者
						event.artist = bytes.readMultiByte(len, "Shift-JIS");
						parent.artist = event.artist;
						break;
					case 0x03:	//タイトル
						event.title = bytes.readMultiByte(len, "Shift-JIS");
						parent.title = event.title;
						break;
					case 0x2F:	//トラック終了
						break main;
					case 0x51:	//テンポ
						event.tempo = bytes.readUnsignedByte()*0x10000 + bytes.readUnsignedShort();
						if(parent.tempo == 0) {
							parent.tempo = 60000000 / event.tempo;
						}
						break;
					case 0x58:	//拍子
						parent.signature_n = bytes.readUnsignedByte();
						parent.signature_d = Math.pow(2, bytes.readUnsignedByte());
						bytes.position += 2;
						break;
					default:
						bytes.position += len;
						break;
					}
				}
				else if(temp == 0xF0 || temp == 0xF7)	//Sysx
				{
					event.type = temp;
					len = readVariableLength();
					event.sysx = new ByteArray();
					bytes.readBytes(event.sysx, 0, len);
				}
				else {
					if(temp & 0x80) {
						type = temp & 0xF0;
						channel = temp & 0x0F;
					}
					else {
						bytes.position--;
					}
					
					event.type = type;
					event.channel = channel;
					
					switch(type)
					{
					case 0x80:	//ノートオフ
						event.note = bytes.readUnsignedByte();
						event.velocity = bytes.readUnsignedByte();
						break;
					case 0x90:	//ノートオン
						event.note = bytes.readUnsignedByte();
						event.velocity = bytes.readUnsignedByte();
						break;
					case 0xA0:	//ポリフォニックキープレッシャー
						event.note = bytes.readUnsignedByte();
						event.value = bytes.readUnsignedByte();
						break;
					case 0xB0:	//コントロールチェンジ
						event.cc = bytes.readUnsignedByte();
						event.value = bytes.readUnsignedByte();
						break;
					case 0xC0:	//パッチチェンジ
						event.value = bytes.readUnsignedByte();
						break;
					case 0xD0:	//チャンネルプレッシャー
						event.value = bytes.readUnsignedByte();
						break;
					case 0xE0:	//ピッチベンド
						event.lsb = bytes.readUnsignedByte();
						event.msb = bytes.readUnsignedByte();
						break;
					}
				}
				sequence.push(event);
			}
			length = time;
		}
		
		public function toString():String
		{
			var text:String = length + "\n";
			
			for(var i:int = 0; i < sequence.length; i++)
			{
				if(sequence[i].toString() == "") {continue;}
				text += sequence[i].toString();
			}
			
			return text;
		}
	}

	dynamic class SMFEvent
	{
		public var delta_time:uint;	//相対時間
		public var time:uint;	//絶対時間
		public var type:int;
		
		public function toString():String
		{
			var text:String = "";
			//text = type.toString(16);
			//*
			switch(type)
			{
			case 0x90:
				if(this.velocity == 0) {break;}
				
				switch(this.note % 12)
				{
				case  0: text += "ド"; break;
				case  1: text += "ド#"; break;
				case  2: text += "レ"; break;
				case  3: text += "ミb"; break;
				case  4: text += "ミ"; break;
				case  5: text += "ファ"; break;
				case  6: text += "ファ#"; break;
				case  7: text += "ソ"; break;
				case  8: text += "ソ#"; break;
				case  9: text += "ラ"; break;
				case 10: text += "シb"; break;
				case 11: text += "シ"; break;
				}
				//text += " "+this.velocity;
				break;
			case 0xB0:
				text += "CC#" + this.cc +" "+this.value + " ";
				break;
			case 0xC0:
				text += "楽器変更 " + this.value + " ";
				break;
			case 0xF0:
			case 0xF7:
				text += "Sysx : ";
				for(var i:int = 0; i < this.sysx.length; i++) {
					text += this.sysx[i].toString(16)+" ";
				}
				break;
			}
			/*/
			if(type == 0x90 && this.velocity != 0)
			{
				switch(this.note % 12)
				{
				case  0: text += "c"; break;
				case  1: text += "c+"; break;
				case  2: text += "d"; break;
				case  3: text += "d+"; break;
				case  4: text += "e"; break;
				case  5: text += "f"; break;
				case  6: text += "f+"; break;
				case  7: text += "g"; break;
				case  8: text += "g+"; break;
				case  9: text += "a"; break;
				case 10: text += "a+"; break;
				case 11: text += "b"; break;
				}
			}
			//*/
			return text;
		}
	}
	
	class SMFBinary
	{
		public static const data:String = "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"
		 + "H85DH2s";
	}

Forked