投稿者 るきお  (社会人) 投稿日時 2010/6/4 22:24:12
続きです。


Public Class MIDITrack

    Protected ChunkType As New List(Of ByteFrom {&H4D, &H54, &H72, &H6B}
    Protected DataLength As New List(Of ByteFrom {0, 0, 0, 0}
    Protected Data As List(Of Byte)
    Protected Footer As New List(Of ByteFrom {0, &HFF, &H2F, 0}

    Public Notes As New List(Of MidiEvent)

    ''' <summary> 
    ''' 音符を追加します。音符は楽譜の先頭から追加していきます。 
    ''' </summary> 
    ''' <param name="note">音階</param> 
    ''' <param name="octave">オクターブ。中央のオクターブは3です。</param> 
    ''' <param name="tick">発音時間。</param> 
    ''' <remarks></remarks> 
    Public Sub AddNote(ByVal note As Notes, ByVal octave As IntegerByVal tick As Integer)
        Dim thisNote As New MidiEvent(note, octave, tick)
        Notes.Add(thisNote)
    End Sub

    ''' <summary> 
    ''' 音符情報をMIDI形式に変換してDataプロパティに格納します。 
    ''' </summary> 
    Protected Overridable Sub GenerateData()

        Const NoteOn As Byte = &H7F
        Const NoteOff As Byte = &H0
        Data = New List(Of Byte)

        For Each note As MidiEvent In Notes
            Data.Add(&H0)   'まずは 0秒後に 
            Data.Add(&H90)  'チャンネル1で 

            Data.Add(note.Note + ((note.Octave - 3) * 12))  'この音階を 
            Data.Add(NoteOn)    'ならし 

            Data.Add(note.Tick) 'Tick時間後に 

            Data.Add(note.Note + ((note.Octave - 3) * 12))  'この音階を 
            Data.Add(NoteOff)   '停止する 
        Next

    End Sub

    ''' <summary> 
    ''' フッター4バイトを含むデータ部の長さを4バイトの領域に記録する。 
    ''' </summary> 
    Private Sub CalculateLength()

        'もっと簡単な方法があると思うが眠たい…。 
        '非効率だがこれでも機能的にはOKなはず。 
        Dim length As Integer = Data.Count + 4

        DataLength(3) = length Mod 256
        length = length >> 8
        DataLength(2) = length Mod 256

        length = length >> 8
        DataLength(1) = length Mod 256

        length = length >> 8
        DataLength(0) = length
    End Sub

    ''' <summary> 
    ''' このトラックが表すすべてのデータをバイト型の配列で表現する。 
    ''' </summary> 
    Public Overridable Function ToList() As List(Of Byte)

        GenerateData()
        calculatelength()

        Dim all As New List(Of Byte)
        all.AddRange(ChunkType)
        all.AddRange(DataLength)
        all.AddRange(Data)
        all.AddRange(Footer)

        Return all
    End Function

End Class

''' <summary> 
''' テンポ調整用トラック。固定で120にしています。 
''' プロパティで可変にするのがよく、さらに任意のタイミングでテンポを切り替えられるようにするとベストです。 
''' </summary> 
Public Class ConductorTrack
    Inherits MIDITrack

    Protected Overrides Sub GenerateData()
        Data = New List(Of ByteFrom {0, &HFF, &H51, 3, 7, &HA1, &H20}
    End Sub
End Class

''' <summary>音符を表します。</summary> 
Public Class MidiEvent
    ''' <summary>音階</summary> 
    Public Note As Notes
    ''' <summary>オクターブ</summary> 
    Public Octave As Integer
    ''' <summary>発音時間(長さ)</summary> 
    Public Tick As Integer

    ''' <summary> 
    ''' 音符を作ります。 
    ''' </summary> 
    ''' <param name="note">音階</param> 
    ''' <param name="octave">オクターブ。中央のオクターブは3です。</param> 
    ''' <param name="tick">発音時間。</param> 
    ''' <remarks></remarks> 
    Public Sub New(ByVal note As Notes, ByVal octave As IntegerByVal tick As Integer)
        Me.Note = note
        Me.Octave = octave
        Me.Tick = tick
    End Sub
End Class

''' <summary>音階を表します。</summary> 
Public Enum Notes As Byte
    ''' <summary>ド</summary> 
    C = &H3C
    ''' <summary>ド#</summary> 
    Cs = &H3D
    ''' <summary>レ</summary> 
    D = &H3E
    ''' <summary>レ#</summary> 
    Ds = &H3F
    ''' <summary>ミ</summary> 
    E = &H40
    ''' <summary>ファ</summary> 
    F = &H41
    ''' <summary>ファ#</summary> 
    Fs = &H42
    ''' <summary>ソ</summary> 
    G = &H43
    ''' <summary>ソ#</summary> 
    Gs = &H44
    ''' <summary>ラ</summary> 
    A = &H45
    ''' <summary>ラ#</summary> 
    [As] = &H46
    ''' <summary>シ</summary> 
    B = &H47
End Enum