Max 5 API Reference

ファイルの取り扱い

Max には、ファイル処理を行なうクロスプラットフォームなルーチンのセットが含まれています

これらのルーチンを使うと、ファイルを開き、読み込み、書き出し、閉じるといった処理を行なうことができるだけでなく、ファイルの検索、ファイルを開くダイアログや保存するダイアログの表示を行なうこともできます。ファイル API は 「パス識別子(path identifier)」に基づいています。パス識別子はファイルの位置を記述する数値です。ファイルの検索や読み込みを行なう場合、パス識別子はフォルダ、あるいはコレクティブになります。負(またはゼロ)の値を持つパス識別子はコンピュータのファイルシステムにある実際のフォルダを指します。これに対し、正の値を持つパス識別子はコレクティブを指します。

基本的に、オブジェクトに対して行なうべきことは、既存の Max オブジェクトと同様な方法で read メッセージを処理できるようにするということです。read という 語( word )の後にアーギュメントがない場合、ユーザがファイルの選択を行なえるようにファイルダイアログを表示します。read の後にアーギュメントが1つある場合には、オブジェクトはファイルの検索を行ないます。そして、ファイルが見つかった(あるいは選択された)場合に、オブジェクトはそのファイルを開き、そこからデータを読み込みます。

まず、オブジェクトが read メッセージを受け取ることができるようにしましょう。ファイル名を示すアーギュメントをオプションとして実装する最も簡単な方法は、A_DEFSYM というアーギュメントの型指定子を使用することです。アーギュメントとしてシンボルが与えられない場合、Max はこのメソッドに対し、空のシンボルを渡します。

        class_addmethod(c, (method)myobject_read, "read", A_DEFSYM, 0);

ファイルの読み込みを行なうあらゆるメソッドで次に必要なことは、次の実装例のようにdefer によって低い優先度のスレッドで実行させるようにしなければならないということです。この例ではファイル名を表すアーギュメントはシンボルアーギュメントとして defer に渡されています。

    void myobject_read(t_myobject *x, t_symbol *s)
    {
        defer(x, (method)myobject_doread, s, 0, NULL);
    }

myobject_doread() 関数では、ファイル名のアーギュメントを空のシンボルと比較しています。アーギュメントが与えられなかった場合、 open_dialog()を使用します。アーギュメントが与えられた場合には、 locatefile_extended() を呼び出してファイルの検索を行ないます。このオブジェクトはテキストファイルを検索するため、開いたり、検索したりするファイルのタイプとして4文字のキャラクタコード 'TEXT' を使っています。ファイルタイプコードは、対象とする拡張子のセットを定義します。max-fileformats.txt というファイルには標準的な定義が書かれています。同様なテキストファイルを作成してCycling '74 フォルダの中の init フォルダ内に置くことにより、独自の設定を追加することもできます。

    void myobject_doread(t_myobject *x, t_symbol *s)
    {
        long filetype = 'TEXT', outtype;
        short numtypes = 1;
        char filename[512];
        short path;

        if (s == gensym("")) {      // アーギュメントが与えられない場合、ファイル名を尋ねます
            if (open_dialog(filename, &path, &outtype, &filetype, 1))       // 非ゼロの値: ユーザによりキャンセルされた場合
                return;
        } else {
            strcpy(filename, s->s_name);    // locatefile_extended を呼び出す前にシンボルをコピーしなければなりません
            if (locatefile_extended(filename, &path, &outtype, &filetype, 1)) { // 非ゼロの値:ファイルが見つからなかった場合
                object_error(x, "%s: not found", s->s_name);
                return;
            }
        }
        // ファイルが確定された場合
        myobject_openfile(x, filename, path);
    }

ファイルを開いたり、呼び出したりする場合、クロスプラットフォームなシステム API を使用することができます。ファイルはファイル名+パス識別子を使って開くことができます。ファイルを開くことに成功すると t_filehandle を使ってファイルにアクセスすることができます。コレクティブファイルの中の「ファイル」も通常のファイルと同様に扱われるという点に注意して下さい。ただし、コレクティブファイルの中のファイルは読み出し専用(リードオンリー)になります。

テキストファイルの読み込み

まず、テキストファイルのファイル名とパス識別子が myobject_openfile()に渡される場合のテキストファイル読み込みを実装します。ここでは、sysfile_readtextfile() という高レベルのルーチンを使っています。このルーチンは、テキスト・エンコーディングの変換処理を行なってくれるという特徴を持っています。テキスト・エンコーディングの変換は控えめに言ってもやっかいなものです。そのため、テキストファイルの読み込みにはこのルーチンの使用を強く推奨します。

    void myobject_openfile(t_myobject *x, char *filename, short path)
    {
        t_filehandle fh;
        char **texthandle;

        if (path_opensysfile(filename, path, &fh, READ_PERM)) {
            object_error(x, "error opening %s", filename);
            return
        }
        // allocate some empty memory to receive text
        texthandle = sysmem_newhandle(0);
        sysfile_readtextfile(fh, texthandle, 0, 0);     // see flags explanation below
        post("the file has %ld characters",sysmem_gethandlesize(texthandle));
        sysfile_close(fh);
        sysmem_freehandle(texthandle);
    }

たいていの場合、sysfile_readtextfile() には最後の2つの引数として 0 を渡します。第3の引数は、読み込みを行なう最大長を指定するものですが、この値が 0 である場合、ファイルのサイズに関係なくファイル全体が読み込まれます。最後の引数はテキスト読み込みの際のオプションを指定するフラグをセットするためのものです。このオプションは、改行やテキスト・エンコーディングの変換、および返されたデータの最後に NULL文字を追加する機能に関するものです。

テキストファイルを読み込む際、改行は任意の改行フラグに基づいて変換されます。Max は改行を「ネイティブ」なフォーマットに変換します。例えば、Windows では

 \r\n 

に変換され、Mac では

 \n 

に変換されます。改行フラグを渡さない場合、および TEXT_LB_NATIVE を渡した場合にはこのように動作します。他のオプションとしてTEXT_LB_MACTEXT_LB_UNIXTEXT_LB_PC があります。

デフォルトでは、テキストファイルはソースのエンコーディングから UTF-8 に変換されます。この変換が望ましくない場合には、TEXT_ENCODING_USE_FILE フラグを使用します。この場合、エンコーディングの決定はあなた自身の責任になりますが、おそらくこのような処理を望むケースはほとんどないでしょう。例えば、UTF-16 を使用したソーステキストファイルの場合、8ビットエンコーディングとは大きく異なった構文解析が必要になります。

最後に、TEXT_NULL_TERMINATE フラグを使用すると sysfile_readtextfile() から返されたメモリの最後に NULL 文字を追加することができます。

データファイルの読み込み

データファイルを読み込む際にテキストエンコーディング変換を行って欲しくない場合や、改行コードについて心配がある場合、 sysfile_readtextfile() の代わりに sysfile_read() を使って myobject_openfile 関数を書くことにより、上記のテキストファイルと同様なテクニックを使用できます。次の例では、ファイル全体を単一のメモリブロックに読み込む方法を示しています。

    void myobject_openfile(t_myobject *x, char *filename, short path)
    {
        t_filehandle fh;
        char *buffer;
        long size;

        if (path_opensysfile(filename, path, &fh, READ_PERM)) {
            object_error(x, "error opening %s", filename);
            return
        }
        // ァイルのサイズ分のメモリブロックを割り当てます
        sysfile_geteof(fh, &size);
        buffer = sysmem_newptr(size);

        // ファイルの内容を読み込みます
        sysfile_read(fh, &size, buffer);

        sysfile_close(fh);

        // このバッファ内で何らかのデータ処理を行ないます

        sysmem_freeptr(buffer);     // 割り当てたメモリを解放しなければなりません
    }

ファイルの書き出し

Max オブジェクトの中には、データをファイルに保存するための write メッセージに応答するものがあります。この語( word) "write" に続くアーギュメントがない場合、ファイル保存ダイアログが表示され、これを使ってユーザがファイルの場所とファイル名を指定することができます。アーギュメントがある場合、このアーギュメントでは完全なパス名、またはファイル名を指定することができます。ファイル名の場合、このファイルは現在の「デフォルト」ディレクトリに書き出されます。デフォルトのディレクトリは、パッチが最後に開かれた場所です。フルパス名の場合、このパス名で指定された場所にファイルが書き出されます。

次の例は、この処理の実装方法を示したものです。ここではメッセージのアーギュメントの処理方法を示し、次にテキストファイルやデータファイルを書き出す例を示しています。

メッセージとアーギュメントの処理は、defer による実行も含め、前に示した read メッセージの実装の場合と非常に良く似た方法で行なっています。

    class_addmethod(c, (method)myobject_write, "write", A_DEFSYM, 0);

    void myobject_write(t_myobject *x, t_symbol *s)
    {
        defer(x, (method)myobject_dowrite, s, 0, NULL);
    }

myobject_dowrite() 関数では、ファイル名のアーギュメントを空のシンボルと比較しています。アーギュメントが与えられなかった場合、 saveasdialog_extended() を使って、ユーザが選んだファイル名と場所の情報を得ます。最初の例ではテキストファイルの検索を行なうために4文字のコード 'TEXT'を使いました。そのため、ここではこの4文字コード 'TEXT' を保存するファイルのファイルタイプとして使用します。ファイルタイプコードは、対象とする拡張子のセットを定義します。max-fileformats.txt というファイルには標準的な定義が書かれています。同様なテキストファイルを作成し、Cycling '74 フォルダの中の init フォルダ内に置くことにより、独自の設定を追加することもできます。

    void myobject_dowrite(t_myobject *x, t_symbol *s)
    {
        long filetype = 'TEXT', outtype;
        short numtypes = 1;
        char filename[512];
        short path;

        if (s == gensym("")) {      // アーギュメントがない場合、ファイル名を尋ねます
            if (saveasdialog_extended(filename, &path, &outtype, &filetype, 1))     // 非ゼロの値:ユーザによりキャンセルされた場合
                return;
        } else {
            strcpy(filename, s->s_name);
            path = path_getdefault();
        }
        myobject_writefile(x, filename, path);
    }

次は、高レベルの sysfile_writetextfile() ルーチンを使用した、myobject_writefile() という別のテキストファイル書き出し関数の例です。ここでは、単に1つの文をテキストファイルに書き出しているだけですが、実際には、書き出そうとする何らかのデータが内部に格納されていることでしょう。 sysfile_writetextfile() に渡されるバッファの最後は NULL 文字でなければなりません。また、データが UTF-8 でエンコードされていることが前提になっています。

path_createsysfile() が filename という引数でフルパス名を受け取ることができる点に注意して下さい。この場合、引数 path は無視されます。このことは、オブジェクトの write メッセージがファイル名とフルパス名のどちらでも受け取ることができ、両方を受け取れるようにするために特別な処理を行なう必要がないということを意味します

    void myobject_writefile(t_myobject *x, char *filename, short path)
    {
        char *buf = "write me into a file";
        long err;
        t_filehandle fh;

        err = path_createsysfile(filename, path, 'TEXT', &fh); 
        if (err)
            return;
        err = sysfile_writetextfile(fh, &buf, TEXT_LB_NATIVE);
        sysfile_close(fh);
    }

次は、myobject_writefile() のデータファイル版です。ここでは、10個の数値を持った小さなバッファをファイルに書き出しています。

    void myobject_writefile(t_myobject *x, char *filename, short path)
    {
        char *buf[10];
        long count, i;
        long err;
        t_filehandle fh;

        // データの作成

        for (i = 0; i < 10; i++)
            buf[i] = i + 1;

        count = 10;

        err = path_createsysfile(filename, path, 'TEXT', &fh); 
        if (err)
            return;
        err = sysfile_write(fh, &count, buf);
        sysfile_close(fh);
    }

Copyright © 2008, Cycling '74