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

Standard MIDI File をロードする

このセクションでは、スタンダードMIDIファイルをロードするためのサポートクラスについて解説します。

スタンダードMIDIファイルは、曲全体に関連する情報を持つヘッダチャンクと、個々のトラックの情報を持つトラックチャンクから成ります。サポートクラスは、ヘッダチャンクとトラックチャンクそれぞれの内容を読み込み、情報を提供します。

ヘッダチャンク

ヘッダチャンクには、”MThd”という4文字の識別子が埋め込まれています。これを手がかりにヘッダチャンクであることを識別します。

ヘッダチャンクには、以下の情報が含まれます。

HeaderChunkクラスは、ヘッダチャンクであるかどうかを識別し、これらの情報を取得します。

スタンダードMIDIファイルは、数値がビッグエンディアンで記録されています。Endianクラスは、ビッグエンディアンとリトルエンディアンの変換をサポートするヘルパークラスです。
class CHeaderChunk
{
public:
    CHeaderChunk(void);
    virtual ~CHeaderChunk(void);

public:
    int         m_Length;
    short       m_Format;
    short       m_Tracks;
    short       m_TPQN;

private:
    static BYTE     m_MThd[];

public:
    bool    Load(BYTE* data, int& pos);
};
bool CHeaderChunk::Load(
    BYTE*       data,
    int&        pos)
{
    for (int i=0; i<4; i++)
    {
        if (data[i + pos] != m_MThd[i])     return false;
    }
    pos += 4;

    m_Length = CEndian::GetInt(data, pos);      pos += sizeof(int);
    m_Format = CEndian::GetShort(data, pos);    pos += sizeof(short);
    m_Tracks = CEndian::GetShort(data, pos);    pos += sizeof(short);
    m_TPQN   = CEndian::GetShort(data, pos);    pos += sizeof(short);

    return true;
}

BYTE CHeaderChunk::m_MThd[] = { (BYTE)'M',(BYTE)'T',(BYTE)'h',(BYTE)'d' };
    public class HeaderChunk
    {
        public int      Length { get; private set; }
        public short    Format { get; private set; }
        public short    Tracks { get; private set; }
        public short    TPQN { get; private set; }

        private static byte[] MThd = { (byte)'M',(byte)'T',(byte)'h',(byte)'d' };

        public bool Load(
            byte[]      data,
            ref int     pos)
        {
            for (int i=0; i<MThd.Length; i++)
            {
                if (data[i + pos] != MThd[i])   return false;
            }
            pos += MThd.Length;

            Length = Endian.GetInt(data, pos);      pos += sizeof(int);
            Format = Endian.GetShort(data, pos);    pos += sizeof(short);
            Tracks = Endian.GetShort(data, pos);    pos += sizeof(short);
            TPQN   = Endian.GetShort(data, pos);    pos += sizeof(short);

            return true;
        }
    }

トラックチャンク

トラックチャンクには、”MTrk”という4文字の識別子が埋め込まれています。これを手がかりにトラックチャンクであることを識別します。

トラックチャンクには、以下の情報が含まれます。

TrackChunkクラスは、トラックチャンクであるかどうかを識別し、これらの情報を取得します。トラックチャンクは、フォーマット1では複数あるため、TrackChunkクラスをトラックの数だけ用意します。

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

public:
    int         m_Length;
    BYTE*       m_pData;

private:
    static BYTE     m_MTrk[];

public:
    bool    Load(BYTE* data, int& pos);
};

トラックの長さは可変長であるため、Load関数では、バイト配列の作業エリアを割り当てています。このエリアはディストラクタで開放するようにします。

CTrackChunk::CTrackChunk(void)
{
    m_Length = 0;
    m_pData = NULL;
}

CTrackChunk::~CTrackChunk(void)
{
    delete m_pData;
}

bool CTrackChunk::Load(
    BYTE*       data,
    int&        pos)
{
    for (int i=0; i<4; i++)
    {
        if (data[i + pos] != m_MTrk[i])     return false;
    }
    pos += 4;

    m_Length = CEndian::GetInt(data, pos);
    pos += sizeof(int);

    m_pData = new BYTE[m_Length];
    CopyMemory(m_pData, data + pos, m_Length);
    pos += m_Length;

    return true;
}

BYTE CTrackChunk::m_MTrk[] = { (BYTE)'M', (BYTE)'T',(BYTE)'r',(BYTE)'k' };
    public class TrackChunk
    {
        public int      Length { get; private set; }
        public byte[]   Data { get; private set; }

        private static byte[] MTrk = { (byte)'M',(byte)'T',(byte)'r',(byte)'k' };

        public bool Load(
            byte[]      data,
            ref int     pos)
        {
            for (int i=0; i<MTrk.Length; i++)
            {
                if (data[i + pos] != MTrk[i])   return false;
            }
            pos += MTrk.Length;

            Length = Endian.GetInt(data, pos);
            pos += sizeof(int);

            Data = new byte[Length];
            Array.Copy(data, pos, Data, 0, Length);
            pos += Length;

            return true;
        }
    }

データを読み込む

LoadSMFクラスは、スタンダードMIDIファイルを読み込むためのサービスクラスです。

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

public:
    CHeaderChunk                        m_HeaderChunk;
    CArray <CTrackChunk*, CTrackChunk*> m_pTrackChunkArray;

public:
    Error   Load(LPCTSTR filePath);
};

ファイル全体を読み込んだら、ヘッダチャンクをロードし、続けてトラックチャンクをトラックの数だけロードします。トラックの数は可変長なので、TrackChunkクラスは、配列に保存するようにします。

CFileクラスをスタック上に取った場合、ファイルはCFileのディストラクタでクローズされるため、明示的にClose関数をコールしていません。

CLoadSMF::CLoadSMF(void)
{
}

CLoadSMF::~CLoadSMF(void)
{
    for (int i=0; i<m_pTrackChunkArray.GetCount(); i++)
    {
        CTrackChunk* tc = m_pTrackChunkArray[i];
        delete tc;
    }
}

Error CLoadSMF::LoadChunk(LPCTSTR filePath)
{
    CFile cf;
    BOOL open = cf.Open(filePath, CFile::modeRead);
    if (open == FALSE)
    {
        return FileNotFound;
    }
    CTempMem tm((int)cf.GetLength());
    cf.Read(tm.m_pData, (UINT)cf.GetLength());

    int pos = 0;
    if (m_HeaderChunk.Load(tm.m_pData, pos) == false)
    {
        return SMFFormat;
    }
    for (int track=0; track<m_HeaderChunk.m_Tracks; track++)
    {
        CTrackChunk* tc = new CTrackChunk();
        m_pTrackChunkArray.Add(tc);

        if (tc->Load(tm.m_pData, pos) == false)
        {
            return SMFFormat;
        }
    }
    return NoErr;
}
CTempMemクラスは、作業用バッファを支援するヘルパークラスです。

LoadChunk関数では、TrackChunkクラスをトラックの数だけnewしています。LoadSMFクラスを破棄するときは、TrackChunkクラスをdeleteしなければなりません。

    public class LoadSMF
    {
        public HeaderChunk      HeaderChunk { get; private set; }
        public TrackChunk[]     TrackChunk { get; private set; }

        public int Tracks { get { return HeaderChunk.Tracks; } }

        public Error LoadChunk(string filePath)
        {
            if (File.Exists(filePath) == false)
            {
                return Error.FileNotFound;
            }
            using (FileStream fs = File.OpenRead(filePath))
            {
                byte[] data = new byte[fs.Length];
                fs.Read(data, 0, data.Length);

                int pos = 0;
                HeaderChunk = new HeaderChunk();

                if (HeaderChunk.Load(data, ref pos) == false)
                {
                    return Error.SMFFormat;
                }
                TrackChunk = new TrackChunk[Tracks];

                for (int track=0; track<Tracks; track++)
                {
                    TrackChunk[track] = new TrackChunk();

                    if (TrackChunk[track].Load(data, ref pos) == false)
                    {
                        return Error.SMFFormat;
                    }
                }
            }
            return Error.NoErr;
        }
    }

FileStreamをusingでラップすると、スコープから抜けるとき、ファイルがクローズされます。そのため明示的にファイルをクローズしていません。

ドキュメントの先頭へ

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