カクタスソフトウェア
カクタスソフトウェア
サウンド MIDI マルチメディア アプリケーション

クラスを用いたMIDIイベントの表現

このセクションでは、クラスを用いたMIDIイベントの表現方法について解説します。

ここで紹介する実装は、基本的なMIDIイベントに、Standard MIDI Fileのメタイベントを加えた構成になっています。これによって、MIDIメッセージの送信・受信と、Standard MIDI Fileのロード・セーブを支援することができます。

さらに高度な機能を支援するときは、追加のイベントが必要になる場合があります。

基底クラス

シーケンサーで用いるイベントには数多くの種類がありますが、それらを一つのストリームやリスト・配列などで管理するために、すべてのイベントの親になる基底クラスを定義します。個々のMIDIイベントやStandard MIDI Fileのメタイベントは、基底クラスから継承したサブクラスで表現します。

基底クラスには、すべてのイベントに共通するプロパティ、およびメソッドを実装します。またオーバーロードして使う可能性のあるメソッドを仮想関数として実装します。

プロパティ

基底クラスに実装するプロパティには以下のものがあります。

ID

クラスがどんな情報を持っているのかを識別するためのIDです。クラスを作成する時に決定される読み出し専用のプロパティです。

IDは、#defineやenumを用いて定義します。イベントの種類と数値の対応は、IDがユニークであれば、どのように決めてもかまいません。以下に例を示します。

enum tagStatus
{
    NopEvent            =   0,          // 0x00: NOP Event

    NoteEvent           =   1,          // 0x01: Note Event

    EndOfTrack          =  15,          // 0x0f: End of Track

    ProgramChange       = 101,          // 0x65: Program Change
    PitchEvent          = 107,          // 0x6b: Pitch Bend
    ChAfterTouch        = 108,          // 0x6c: Channel AfterTouch
    PolyAfterTouch      = 109,          // 0x6d: Polyphonic AfterTouch
    ControlChange       = 102,          // 0x66: Control Change

    ModeMessage         = 112,          // 0x70: Channel Mode message

    TempoEvent          =   8,          // 0x08: Tempo
    TimeSignature       =  35,          // 0x23: TimeSignature
    KeySignature        =  32,          // 0x20: KeySignature
    SMPTEEvent          = 120,          // 0x78: SMPTE

    SystemExclusive     =  96,          // 0x60: System Exclusive

    SequenceNumber      =  80,          // 0x50: Sequence Number
    TextEvent           =  81,          // 0x51: Text Event
    CopyRightEvent      =  82,          // 0x52: Copy Right
    TrackNameEvent      =  83,          // 0x53: Track Name
    InstrumentName      =  84,          // 0x54: Instrument Name
    LyricEvent          =  85,          // 0x55: Lyric
    MarkerEvent         =  86,          // 0x56: Marker
    QuePointEvent       =  87,          // 0x57: Que Point
    ChannelPrefix       =  88,          // 0x58: Channel PreFix

    PortSelectEvent     =  90,          // 0x5a: Port Select Event

    UnknownMetaEvent    = 255,          // 0xff: Unknown Meta Event
};
    public enum Status
    {
        Nop                 =   0,          // 0x00: NOP Event

        Note                =   1,          // 0x01: Note Event

        EndOfTrack          =  15,          // 0x0f: End of Track

        ProgramChange       = 101,          // 0x65: Program Change
        PitchBend           = 107,          // 0x6b: Pitch Bend
        ChAfterTouch        = 108,          // 0x6c: Channel AfterTouch
        PolyAfterTouch      = 109,          // 0x6d: Polyphonic AfterTouch
        ControlChange       = 102,          // 0x66: Control Change

        ModeMessage         = 112,          // 0x70: Channel Mode message

        Tempo               =   8,          // 0x08: Tempo
        TimeSignature       =  35,          // 0x23: TimeSignature
        KeySignature        =  32,          // 0x20: KeySignature
        SMPTE               = 120,          // 0x78: SMPTE

        SystemExclusive     =  96,          // 0x60: System Exclusive

        SequenceNumber      =  80,          // 0x50: Sequence Number
        Text                =  81,          // 0x51: Text Event
        Copyright           =  82,          // 0x52: Copyright
        TrackName           =  83,          // 0x53: Track Name
        InstrumentName      =  84,          // 0x54: Instrument Name
        Lyric               =  85,          // 0x55: Lyric
        Marker              =  86,          // 0x56: Marker
        QuePoint            =  87,          // 0x57: Que Point
        ChannelPrefix       =  88,          // 0x58: Channel PreFix

        PortSelect          =  90,          // 0x5a: Port Select Event

        UnknownMeta         = 255,          // 0xff: Unknown Meta Event
    }

時刻情報

音楽データは、時刻に関連付けられているため、時刻情報は重要なプロパティです。作成する曲の長さを十分表現できるレンジが必要です。通常プラスの値を取りますが、データの途中で位置合せを行うような仕様にしたとき、負の値になることがあります。通常32ビットのintを用います。

イベントの時刻を示すチック位置の他に、演奏表現を付加するために「時刻の差分」を用意することがあります。リストエディターなどではチック位置に従って表示し、演奏するときはチック位置に差分を加算した時刻を用います。

時刻の差分を「デビエーション」と呼びます。デビエーションについてはセクション「演奏表現を付加する」を参照してください。

トラック番号・チャンネル番号

大規模な音楽データの場合、トラックによる管理を行うことが一般的です。また、一つのトラックに複数のチャンネルを含むデータでは、チャンネル番号も併せて記録する必要があります。

フラグ類

イベントを選択したり、特別なマークを付けたいときに用いるフラグです。この実装では、イベントを選択するSelectのみを実装しています。

基底クラスへの実装

プロパティを基底クラスに実装すると、すべてのサブクラスで参照可能になります。

トラック番号は、多くのMIDIイベントで利用しますが、中には必要としないイベントもあります。しかし利用するケースの方が多いという判断で基底クラスに実装しています。

チャンネル番号も、データの中でチャンネルメッセージが占める割合が多いという考えから実装します。

イベント固有のプロパティは、個々のサブクラスに実装すべきですが、複数のサブクラスで利用するプロパティの場合、基底クラスに実装するか、個々のサブクラスに実装するか、という判断をしなければなりません。

class CForm
{
public:
    CForm(void);
    virtual ~CForm(void);

public:
    short   m_Status;
    short   m_Channel;
    short   m_Track;
    BOOL    m_Select;
    long    m_TickPos;
};
CForm::CForm(void)
{
    m_Status  = NopEvent;
    m_Channel = 0;
    m_Track   = 0;
    m_Select  = FALSE;
    m_TickPos = 0;
}

CForm::~CForm(void)
{
}
    public class Form
    {
        public Status   Status { get; internal set; }
        public int      Channel { get; set; }
        public int      Track { get; set; }
        public bool     Select { get; set; }
        public int      TickPos { get; set; }

        public Form()
        {
            Status  = Status.Nop;
            Channel = 0;
            Track   = 0;
            Select  = 0;
            TickPos = 0;
        }
    }

メソッド

メソッドを実装する場合、クラス内部に実装するか、独立したサービス関数として実装するかは、設計の方針しだいです。

支援する機能としては、以下のものが考えられます。

ノートイベント

ノートイベントは、最も重要なイベントです。ノートイベントは、一つの音情報を表現します。以下に主だったプロパティを紹介します。

ノート番号

音の高さを示します。範囲は0から127までで、60が中央のCを示します。

ベロシティ

音の強さを示します。レンジは、1から127までです。この実装では、オンベロシティだけを支援していますが、オフベロシティを支援することも可能です。

デュレーション

音の長さを示します。デュレーションの表現方法は、単にintを用いる方法から、専用のクラスや構造体を用いる方法があります。

録音・演奏のみを支援するアプリケーションの場合、intで問題ありません。楽譜表示を支援するときは、演奏用のデュレーションと、楽譜上の音符の長さの二種類を記録しなければなりません。楽譜表示で用いる音の長さについては、セクション「音符の論理長」を参照してください。

この実装では、一つの音を発音時刻とデュレーションのペアで表現していますが、より簡易な実装では、発音時刻と消音時刻のペアで表現することができます。この場合、ノートイベントは、ノートオンイベントとノートオフイベントの二つが必要になります。

発音時刻とデュレーションを用いると、一つのノートイベントで一つの音を表現することができます。しかし、発音・消音ペアを用いる場合は、ストリームの異なる位置にある二つのノートイベントで表現しなければなりません。

デフォルトとして、ノート番号は60を、ベロシティは72を設定しています。デュレーションのTPQNは、4分音符の長さを示します。

ペンディングフラグ

楽譜表示などで二つの音をタイでつなぐ場合、以下の処置が必要になります。

このノートオフを送信しないことを示すのが、ペンディングオフフラグで、ノートオンを送信しないことを示すのが、ペンディングオンフラグです。これらのフラグは、二つの音をつなぐ機能を支援する場合に必要です。

この実装では簡単のために、ペンディングフラグは支援していません。

class CNoteForm : CForm
{
public:
    CNoteForm(void);
    virtual ~CNoteForm(void);

public:
    short   m_Note;
    short   m_Velocity;
    long    m_Duration;
};
CNoteForm::CNoteForm(void)
{
    m_Status   = NoteEvent;
    m_Note     = 60;
    m_Velocity = 72;
    m_Duration = TPQN;
}

CNoteForm::~CNoteForm(void)
{
}
    public class NoteForm : Form
    {
        public int      Note { get; set; }
        public int      Velocity { get; set; }
        public int      Duration { get; set; }

        public NoteForm()
        {
            Status   = Status.Note;
            Note     = 60;
            Velocity = 72;
            Duration = Constant.TPQN;
        }
    }

コントロールチェンジ

コントロールチェンジは、リバーブやコーラスなどのレベルを設定するイベントです。以下に定義の一例を示します。

enum tagControlChange
{
    BankSelectEvent     =   0,          // 0x00: BankSelect
    ModurationEvent     =   1,          // 0x01: Moduration
    PortamentoTime      =   5,          // 0x05: Portamento Time
    DataEntryEvent      =   6,          // 0x06: DataEntry
    VolumeEvent         =   7,          // 0x07: Volume
    PanPotEvent         =  10,          // 0x0a: PanPot
    ExpressionEvent     =  11,          // 0x0b: Expression
    Hold1Event          =  64,          // 0x40: Hold1
    PortamentoEvent     =  65,          // 0x41: Portamento
    SostenutoEvent      =  66,          // 0x42: Sostenuto
    SoftEvent           =  67,          // 0x43: Soft
    PortamentoCont      =  84,          // 0x54: Portamento Control
    ReverbEvent         =  91,          // 0x5b: Reverb
    ChorusEvent         =  93,          // 0x5d: Chorus
    DelayEvent          =  94,          // 0x5e: Delay
    NRPNEvent           =  99,          // 0x63: NRPN
    RPNEvent            = 101,          // 0x65: RPN
};
    public class ControlChange
    {
        public const int    BankSelect      =   0;      // 0x00: BankSelect
        public const int    Moduration      =   1;      // 0x01: Moduration
        public const int    PortamentoTime  =   5;      // 0x05: Portamento Time
        public const int    DataEntry       =   6;      // 0x06: DataEntry
        public const int    Volume          =   7;      // 0x07: Volume
        public const int    PanPot          =  10;      // 0x0a: PanPot
        public const int    Expression      =  11;      // 0x0b: Expression
        public const int    Hold1           =  64;      // 0x40: Hold1
        public const int    Portamento      =  65;      // 0x41: Portamento
        public const int    Sostenuto       =  66;      // 0x42: Sostenuto
        public const int    Soft            =  67;      // 0x43: Soft
        public const int    PortamentoCont  =  84;      // 0x54: Portamento Control
        public const int    Reverb          =  91;      // 0x5b: Reverb
        public const int    Chorus          =  93;      // 0x5d: Chorus
        public const int    Delay           =  94;      // 0x5e: Delay
        public const int    NRPN            =  99;      // 0x63: NRPN
        public const int    RPN             = 101;      // 0x65: RPN
    }

コントロールチェンジには、バンクセレクトやRPN・NRPNなどの機能が含まれます。これらのイベントは、MSB・LSBと二つのデータが対になって一つの意味を成しているため、特に扱いが厄介です。

コントロールチェンジをどのように扱うかによって設定するプロパティが異なります。簡単に実装するときは、コントロール番号とデータ値を記録すれば良いでしょう。

MSB・LSBのペアデータを一つのイベントで管理すると、データ内容の確認や変更が容易になります。この場合、ペアデータを扱うためのコントロールチェンジクラスやコントロールチェンジ構造体を設計します。

詳しいコントロールチェンジの扱い方については、セクション「コントロールチェンジ」を参照してください。

以下の例のCMsbLsbは、MSB・LSBのペアデータを支援するクラスです。

class CControlForm : CForm
{
public:
    CControlForm(void);
    virtual ~CControlForm(void);

public:
    short   m_Control;
    CMsbLsb m_Value;
};
CControlForm::CControlForm(void)
{
    m_Status  = ControlChange;
    m_Control = BankSelectEvent;
};

MsbLsbは、MSB・LSBのペアデータを支援するクラスです。

    public class ControlForm : Form
    {
        public int      Control { get; set; }
        public MsbLsb   Value { get; set; }

        public ControlForm()
        {
            Status  = Status.ControlChange;
            Control = ControlChange.BankSelect;
            Value   = new MsbLsb();
        }
    }

プログラムチェンジ

プログラムチェンジイベントでは、プログラム番号を記録します。プログラム番号で楽器の種類を指定します。プログラム番号に対応する楽器名を戻すメソッドを定義しても良いでしょう。

class CProgramForm : CForm
{
public:
    CProgramForm(void);
    virtual ~CProgramForm(void);

public:
    short   m_Program;
};
CProgramForm::CProgramForm(void)
{
    m_Status  = ProgramChange;
    m_Program = 0;
};

CProgramForm::~CProgramForm(void)
{
}
    public class ProgramForm : Form
    {
        public int      Program { get; set; }

        public ProgramForm()
        {
            Status  = Status.ProgramChange;
            Program = 0;
        }
    }

モードメッセージ

モードメッセージイベントでは、メッセージ番号とデータ値を記録します。

メッセージは高々8つしかないので、#defineやenumを用いて定義しておけば良いでしょう。

enum tagModeMessage
{
    AllSoundOffEvent    = 120,          // 0x78: All Sound Off
    ResAllContEvent     = 121,          // 0x79: Reset All Controller
    AllNoteOffEvent     = 123,          // 0x7b: All Note Off
    OmniOffEvent        = 124,          // 0x7c: OMNI OFF
    OmniOnEvent         = 125,          // 0x7d: OMNI ON
    MONOMode            = 126,          // 0x7e: MONO
    POLYMode            = 127,          // 0x7f: POLY
};
    public enum ModeMessage
    {
        Nop                 = 0,
        AllSoundOffEvent    = 120,          // 0x78: All Sound Off
        ResAllContEvent     = 121,          // 0x79: Reset All Controller
        AllNoteOffEvent     = 123,          // 0x7b: All Note Off
        OmniOffEvent        = 124,          // 0x7c: OMNI OFF
        OmniOnEvent         = 125,          // 0x7d: OMNI ON
        MONOMode            = 126,          // 0x7e: MONO
        POLYMode            = 127,          // 0x7f: POLY
    }
class CModeMessageForm : CForm
{
public:
    CModeMessageForm(void);
    virtual ~CModeMessageForm(void);

public:
    short   m_Message;
    short   m_Value;
};
CModeMessageForm::CModeMessageForm(void)
{
    m_Status  = ModeMessage;
    m_Message = 0;
    m_Value   = 0;
};
    public class ModeMessageForm : Form
    {
        public ModeMessage  Message { get; set; }
        public int          Value { get; set; }

        public ModeMessageForm()
        {
            Status  = Status.ModeMessage;
            Message = ModeMessage.Nop;
            Value   = 0;
        }
    }

ピッチベンド

ピッチベンドは、値の表現に工夫が必要です。MIDIメッセージのベンド値は、14ビットのバイナリで表現しているため、人間には分かり難く、ユーザーインターフェースとして、セント(1/100半音)単位で表現する方が良いでしょう。

ピッチベンドの値は、14ビットの分解能をフルに利用するために、ピッチベンドセンシティビティ(ベンドレンジ)とベンド値、二つの値を用いて表現しています。ピッチベンドセンシティビティは、通常、曲の先頭で定義するため、具体的にピッチの値を得るには、離れた場所の情報を総合する必要があります。ピッチベンドセンシティビティを変更すると、ピッチの値にも影響が及ぶため、運用には注意が必要です。

簡易な実装では、ピッチベンドセンシティビティとベンド値は個別に表現し、正しい値になるかどうかはユーザーの責任とする、という方法があります。

また、ピッチベンドセンシティビティをアプリケーション側で管理し、最適な値になるよう調整する実装法も考えられます。この場合、ユーザーにはセント単位のピッチの値しか見せないようにします。

詳しいピッチベンドの扱い方については、セクション「ピッチベンド」を参照してください。

以下の実装では、MIDIメッセージの値そのものを記録する方法を取っているため、MSB・LSBのペアデータを用いています。セント単位の値との変換には専用の関数を用います。

class CPitchBendForm : CForm
{
public:
    CPitchBendForm(void);
    virtual ~CPitchBendForm(void);

public:
    short   m_Range;
    CMsbLsb m_Value;
};
CPitchBendForm::CPitchBendForm(void)
{
    m_Status = PitchEvent;
    m_Range  = 12;
};

CPitchBendForm::~CPitchBendForm(void)
{
}
    public class PitchBendForm : Form
    {
        public int      Range { get; set; }
        public MsbLsb   Value { get; set; }

        public PitchBendForm()
        {
            Status = Status.PitchBend;
            Range  = 12;
            Value  = new MsbLsb();
        }
    }

ポリフォニックアフタータッチ

ポリフォニックアフタータッチでは、ノート番号とデータ値を記録します。

class CPolyAfterTouchForm : CForm
{
public:
    CPolyAfterTouchForm(void);
    virtual ~CPolyAfterTouchForm(void);

public:
    short   m_Note;
    short   m_Value;
};
CPolyAfterTouchForm::CPolyAfterTouchForm(void)
{
    m_Status = PolyAfterTouch;
    m_Note   = 60;
    m_Value  = 0;
};

CPolyAfterTouchForm::~CPolyAfterTouchForm(void)
{
}
    public class PolyAfterTouchForm : Form
    {
        public int      Note { get; set; }
        public int      Value { get; set; }

        public PolyAfterTouchForm()
        {
            Status = Status.PolyAfterTouch;
            Note   = 60;
            Value  = 0;
        }
    }

チャンネルアフタータッチ

チャンネルアフタータッチでは、データ値を記録します。

class CChAfterTouchForm : CForm
{
public:
    CChAfterTouchForm(void);
    virtual ~CChAfterTouchForm(void);

public:
    short   m_Value;
};
CChAfterTouchForm::CChAfterTouchForm(void)
{
    m_Status = ChAfterTouch;
    m_Value  = 0;
};

CChAfterTouchForm::~CChAfterTouchForm(void)
{
}
    public class ChAfterTouchForm : Form
    {
        public int      Value { get; set; }

        public ChAfterTouchForm()
        {
            Status = Status.ChAfterTouch;
            Value  = 0;
        }
    }

システムエクスクルーシブ

システムエクスクルーシブメッセージでは、メッセージデータを記録します。またデータの長さを戻すプロパティがあれば便利でしょう。

システムエクスクルーシブメッセージは可変長であるため、データの格納場所をサイズに合せてアロケートする必要があります。

C#を用いる場合、ガベージコレクタがあるため実装は楽ですが、C/C++で実装するときは、可変長データを利用する仕組みを考える必要があります。システムエクスクルーシブメッセージをサポートする専用クラスを定義すると良いでしょう。

システムエクスクルーシブメッセージの扱い方については、セクション「システムエクスクルーシブメッセージ」を参照してください。

以下の実装でCSystemExclusiveは、システムエクスクルーシブメッセージを支援するクラスです。

class CSysExForm : CForm
{
public:
    CSysExForm(void);
    virtual ~CSysExForm(void);

public:
    CSystemExclusive    m_SysEx;
};
CSysExForm::CSysExForm(void)
{
    m_Status = SystemExclusive;
};

CSysExForm::~CSysExForm(void)
{
}
    public class SysExForm : Form
    {
        public SystemExclusive  SysEx { get; set; }

        public SysExForm()
        {
            Status = Status.SystemExclusive;
            SysEx  = new SystemExclusive();
        }
    }

これ以降のイベントは、Standard MIDI Fileに固有のメタイベントです。

テンポ

テンポイベントは、シーケンサーの演奏エンジンに対するコマンドです。テンポの値がMIDIメッセージとして送信されることはありません。

Standard MIDI Fileでは、テンポは、4分音符当たりの演奏時間(単位はμ秒)で定義されています。このままでは人間に分かり難いため、アプリケーションでは、1分当たりの4分音符の数(m.m.)で表示すると良いでしょう。運用にあたっては、4分音符当たりの演奏時間と1分当たりの4分音符の数とで変換が必要になります。

テンポの表示・編集を専用のアプリケーションで行う場合、ノートイベントなど他のイベントから独立した、専用ストリームで管理すると良いでしょう。これによってテンポエディターの設計や演奏時間の計算などがやり易くなります。

詳しいテンポの扱い方については、セクション「テンポ」を参照してください。

デフォルトのテンポは120を指定します。

class CTempoForm : CForm
{
public:
        CTempoForm(void);
        virtual ~CTempoForm(void);

public:
        double  m_Value;
};
CTempoForm::CTempoForm(void)
{
    m_Status = TempoEvent;
    m_Value  = 120.0;
};

CTempoForm::~CTempoForm(void)
{
}
    public class TempoForm : Form
    {
        public double   Value { get; set; }

        public TempoForm()
        {
            Status = Status.Tempo;
            Value  = 120.0;
        }
    }

SMPTEオフセット

SMPTEは、ビデオと音声の同期を行うための規格です。SMPTEのモードには以下の種類があります。

enum tagSMPTEMode
{
    Frame24         = 0,
    Frame25         = 1,
    DropFrame30     = 2,
    NonDropFrame30  = 3,
};
    public enum SMPTEMode
    {
        Frame24         = 0,
        Frame25         = 1,
        DropFrame30     = 2,
        NonDropFrame30  = 3,
    }

記録するデータは以下の通りです。

class CSMPTEForm : CForm
{
public:
    CSMPTEForm(void);
    virtual ~CSMPTEForm(void);

public:
    short   m_Mode;     // SMPTEMode
    short   m_Hour;
    short   m_Min;
    short   m_Sec;
    short   m_Frame;
    short   m_Fraction;
};
CSMPTEForm::CSMPTEForm(void)
{
    m_Status   = SMPTEEvent;
    m_Mode     = Frame24;
    m_Hour     = 0;
    m_Min      = 0;
    m_Sec      = 0;
    m_Frame    = 0;
    m_Fraction = 0;
};

CSMPTEForm::~CSMPTEForm(void)
{
}
    public class SMPTEForm : Form
    {
        public SMPTEMode    Mode { get; set; }
        public int          Hour { get; set; }
        public int          Min { get; set; }
        public int          Sec { get; set; }
        public int          Frame { get; set; }
        public int          Fraction { get; set; }

        public SMPTEForm()
        {
            Status   = Status.SMPTE;
            Mode     = SMPTEMode.Frame24;
            Hour     = 0;
            Min      = 0;
            Sec      = 0;
            Frame    = 0;
            Fraction = 0;
        }
    }

拍子

拍子イベントは、Standard MIDI Fileのメタイベントの一つで、狭義のMIDI規格の中には含まれません。ピアノロールやリストエディターで小節や拍の位置を決めるために用います。拍子イベントは、MIDIメッセージとして送信されることはありません。

SMFで一拍の長さは、2のべき乗で表されるため、以下のように定義しておきます。

enum tagTimeSignature
{
    TimeSigNote0    = 0,    // 全音符
    TimeSigNote2    = 1,    // 2分音符
    TimeSigNote4    = 2,    // 4分音符
    TimeSigNote8    = 3,    // 8分音符
    TimeSigNote16   = 4,    // 16分音符
    TimeSigNote32   = 5,    // 32分音符
};
    public enum TimeSignature
    {
        Note0  = 0,     // 全音符
        Note2  = 1,     // 2分音符
        Note4  = 2,     // 4分音符
        Note8  = 3,     // 8分音符
        Note16 = 4,     // 16分音符
        Note32 = 5,     // 32分音符
    }

記録するデータは以下の通りです。

これに加えて、このイベントが置かれている場所が何小節目になるかを記録しておくと便利です。ただし拍子イベントを変更すると、以降の小節番号がすべて影響を受けるので注意が必要です。また、拍子イベント、つまり小節管理をテンポイベントのように独立して行う方法もあります。

class CTimeSigForm : CForm
{
public:
    CTimeSigForm(void);
    virtual ~CTimeSigForm(void);

public:
    short   m_Num;
    short   m_Den;      // TimeSigNote
    short   m_CC;
    short   m_BB;
    long    m_Measure;
};
CTimeSigForm::CTimeSigForm(void)
{
    m_Status  = TimeSignature;
    m_Num     = 4;
    m_Den     = TimeSigNote4;
    m_CC      = MIDIClock;
    m_BB      = 8;
    m_Measure = 0;
};

CTimeSigForm::~CTimeSigForm(void)
{
}
    public class TimeSigForm : Form
    {
        public int              Num { get; set; }
        public TimeSignature    Den { get; set; }
        public int              CC { get; set; }
        public int              BB { get; set; }
        public int              Measure { get; set; }

        public TimeSigForm()
        {
            Status  = Status.TimeSignature;
            Num     = 4;
            Den     = TimeSignature.Note4;
            CC      = Constant.MIDIClock;
            BB      = 8;
            Measure = 0;
        }
    }

調性

調性イベントは、Standard MIDI Fileのメタイベントの一つです。このイベントは、狭義のMIDI規格の中には含まれません。調性が分かれば、簡易な楽譜表示を行うことが可能です。しかし、正式な楽譜表示を支援するには、このイベントだけでは不十分です。調性イベントは、MIDIメッセージとして送信されることはありません。

長調・短調は、以下のように定義しておきます。

enum tagKeySignature
{
    KeySigMajor = 0,    // 長調
    KeySigMinor = 1,    // 短調
};
    public enum KeySignature
    {
        Major = 0,      // 長調
        Minor = 1,      // 短調
    }

記録するデータは以下の通りです。

class CKeySigForm : CForm
{
public:
    CKeySigForm(void);
    virtual ~CKeySigForm(void);

public:
    short   m_SharpFlat;
    short   m_KeySig;       // KeySignature
};
CKeySigForm::CKeySigForm(void)
{
    m_Status    = KeySignature;
    m_SharpFlat = 0;
    m_KeySig    = KeySigMajor;
};

CKeySigForm::~CKeySigForm(void)
{
}
    public class KeySigForm : Form
    {
        public int          SharpFlat { get; set; }
        public KeySignature KeySig { get; set; }

        public KeySigForm()
        {
            Status    = Status.KeySignature;
            SharpFlat = 0;
            KeySig    = KeySignature.Major;
        }
    }

ポートセレクト

ポートセレクトは、複数の再生デバイスを用いるとき、どのデバイスで再生するかを指定します。Standard MIDI Fileのメタイベントの一つですが、非公式の拡張イベントです。

記録するデータは、ポート番号です。

class CPortSelectForm : CForm
{
public:
    CPortSelectForm(void);
    virtual ~CPortSelectForm(void);

public:
    short   m_Port;
};
CPortSelectForm::CPortSelectForm(void)
{
    m_Status = PortSelectEvent;
    m_Port   = 0;
};

CPortSelectForm::~CPortSelectForm(void)
{
}
    public class PortSelectForm : Form
    {
        public int      Port { get; set; }

        public PortSelectForm()
        {
            Status = Status.PortSelect;
            Port   = 0;
        }
    }

シーケンス番号

シーケンス番号は、複数のSMFファイルを管理するとき、ファイルのID番号として利用します。

class CSeqNumForm : CForm
{
public:
    CSeqNumForm(void);
    virtual ~CSeqNumForm(void);

public:
    long    m_Number;
};
CSeqNumForm::CSeqNumForm(void)
{
    m_Status = SequenceNumber;
    m_Number = 0;
};

CSeqNumForm::~CSeqNumForm(void)
{
}
    public class SeqNumForm : Form
    {
        public int      Number { get; set; }

        public SeqNumForm()
        {
            Status = Status.SequenceNumber;
            Number = 0;
        }
    }

テキスト

Standard MIDI Fileに、テキスト関連のメタイベントがあります。テキストイベントをクラスで管理する場合、構造が似ているため、テキスト基底クラスを定義し、そこから派生させると良いでしょう。以下にイベントを示します。

// テキスト基底クラス

class CTextForm : public CForm
{
public:
    CTextForm(void);
    virtual ~CTextForm(void);

public:
    CString m_Text;
};

// テキストイベント

class CTextEventForm : CTextForm
{
public:
    CTextEventForm(void);
    virtual ~CTextEventForm(void);
};

// 著作権

class CCopyRightForm : CTextForm
{
public:
    CCopyRightForm(void);
    virtual ~CCopyRightForm(void);
};

// トラック名

class CTrackNameForm : CTextForm
{
public:
    CTrackNameForm(void);
    virtual ~CTrackNameForm(void);
};

// 楽器名

class CInstrumentForm : CTextForm
{
public:
    CInstrumentForm(void);
    virtual ~CInstrumentForm(void);
};

// 歌詞

class CLyricForm : CTextForm
{
public:
    CLyricForm(void);
    virtual ~CLyricForm(void);
};

// マーカー

class CMarkerForm : CTextForm
{
public:
    CMarkerForm(void);
    virtual ~CMarkerForm(void);
};

// キューポイント

class CQuePointForm : CTextForm
{
public:
    CQuePointForm(void);
    virtual ~CQuePointForm(void);
};
// テキスト基底クラス

CTextForm::CTextForm(void)
{
    m_Text = "";
};

CTextForm::~CTextForm(void)
{
}

// テキストイベント

CTextEventForm::CTextEventForm(void)
{
    m_Status = TextEvent;
};

CTextEventForm::~CTextEventForm(void)
{
}

// 著作権

CCopyRightForm::CCopyRightForm(void)
{
    m_Status = CopyRightEvent;
};

CCopyRightForm::~CCopyRightForm(void)
{
}

// トラック名

CTrackNameForm::CTrackNameForm(void)
{
    m_Status = TrackNameEvent;
};

CTrackNameForm::~CTrackNameForm(void)
{
}

// 楽器名

CInstrumentForm::CInstrumentForm(void)
{
    m_Status = InstrumentName;
};

CInstrumentForm::~CInstrumentForm(void)
{
}

// 歌詞

CLyricForm::CLyricForm(void)
{
    m_Status = LyricEvent;
};

CLyricForm::~CLyricForm(void)
{
}

// マーカー

CMarkerForm::CMarkerForm(void)
{
    m_Status = MarkerEvent;
};

CMarkerForm::~CMarkerForm(void)
{
}

// キューポイント

CQuePointForm::CQuePointForm(void)
{
    m_Status = QuePointEvent;
};

CQuePointForm::~CQuePointForm(void)
{
}
    // テキスト基底クラス

    public class TextForm : Form
    {
        public string   Text { get; set; }

        public TextForm()
        {
            Text = "";
        }
    }

    // テキストイベント

    public class TextEventForm : TextForm
    {
        public TextEventForm()
        {
            Status = Status.Text;
        }
    }

    // 著作権

    public class CopyRightForm : TextForm
    {
        public CopyRightForm()
        {
            Status = Status.Copyright;
        }
    }

    // トラック名

    public class TrackNameForm : TextForm
    {
        public TrackNameForm()
        {
            Status = Status.TrackName;
        }
    }

    // 楽器名

    public class InstrumentForm : TextForm
    {
        public InstrumentForm()
        {
            Status = Status.InstrumentName;
        }
    }

    // 歌詞

    public class LyricForm : TextForm
    {
        public LyricForm()
        {
            Status = Status.Lyric;
        }
    }

    // マーカー

    public class MarkerForm : TextForm
    {
        public MarkerForm()
        {
            Status = Status.Marker;
        }
    }

    // キューポイント

    public class QuePointForm : TextForm
    {
        public QuePointForm()
        {
            Status = Status.QuePoint;
        }
    }

チャンネルプリフィックス

チャンネルプリフィックスは、チャンネル情報を持たないイベントにチャンネル情報を与えるイベントです。

記録するデータは、チャンネル番号です。この実装では、基底クラスにあるチャンネル番号にデータを記録しています。そのためChPrefixFormクラスにチャンネル番号の変数はありません。

class CChPrefixForm : CForm
{
public:
    CChPrefixForm(void);
    virtual ~CChPrefixForm(void);
};
CChPrefixForm::CChPrefixForm(void)
{
    m_Status = ChannelPrefix;
};

CChPrefixForm::~CChPrefixForm(void)
{
}
    public class ChPrefixForm : Form
    {
        public ChPrefixForm()
        {
            Status  = Status.ChannelPrefix;
        }
    }

終了位置

終了位置は、曲の終了位置を示すイベントです。チック位置が分かれば良いので、他に記録すべきデータはありません。

class CEndOfTrackForm : CForm
{
public:
    CEndOfTrackForm(void);
    virtual ~CEndOfTrackForm(void);
};
CEndOfTrackForm::CEndOfTrackForm(void)
{
    m_Status = EndOfTrack;
};

CEndOfTrackForm::~CEndOfTrackForm(void)
{
}
    public class EndOfTrackForm : Form
    {
        public EndOfTrackForm()
        {
            Status = Status.EndOfTrack;
        }
    }

ドキュメントの先頭へ

カクタスソフトウェア 技術協力 資料室 資料室の広場 SourceForge.jp お問い合わせ