忍者ブログ

Memeplexes

プログラミング、3DCGとその他いろいろについて

[PR]

×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。


OpenAL ファイルイメージからBufferを作成 [C#]

alutCreateBufferFromFileImage
ファイル名からBufferオブジェクトを作るのはalutCreateBufferFromFile関数でしたが、もうちょっと柔軟にできないものでしょうか?

たとえばインターネット上からwavファイルをダウンロードして、それを再生するような場合です。
いちいちハードディスクに保存しなくても再生できないものでしょうか?

alutCreateBufferFromFileImage関数がそれを可能にします。
この関数は、wavファイルのbyte[] からBufferオブジェクトを作成します。
つまり、wavファイルがハードディスク上に保存されている必要はありません。中身のbyte配列がメモリにあればいいのです。

ALuint alutCreateBufferFromFileImage(const ALvoid *data, ALsizei length);

成功すれば生成されたBufferオブジェクトの名前を、失敗すればAL_NONE( = 0)を返します。
using System;
using System.Runtime.InteropServices;

class AudioSource
{
    [DllImport("OpenAL32.dll")]
    static extern void alGenSources(int resultSize, int[] result);

    [DllImport("OpenAL32.dll")]
    static extern void alDeleteSources(int nameCount, int[] sourceNames);

    [DllImport("OpenAL32.dll")]
    static extern void alSourcei(int sourceName, int propertyType, int value);
    const int AL_BUFFER = 0x1009;

    [DllImport("OpenAL32.dll")]
    static extern void alSourcePlay(int sourceName);

    private int name;

    public AudioSource()
    {
        int[] names = new int[1];
        alGenSources(names.Length, names);
        this.name = names[0];
    }

    ~AudioSource()
    {
        alDeleteSources(1, new int[] { this.name });
    }

    public int Buffer
    {
        set
        {
            alSourcei(this.name, AL_BUFFER, value);
        }
    }

    public void Play()
    {
        alSourcePlay(this.name);
    }
}

class AudioBuffer
{
    [DllImport("alut.dll")]
    static extern int alutCreateBufferFromFileImage(byte[] data, int dataLength);

    [DllImport("OpenAL32.dll")]
    static extern void alDeleteBuffers(int nameCount, int[] bufferNames);

    private int name;

    public int Name { get { return this.name; } }

    public AudioBuffer(byte[] fileImage)
    {
        this.name = alutCreateBufferFromFileImage(fileImage, fileImage.Length);
    }

    ~AudioBuffer()
    {
        alDeleteBuffers(1, new int[] { name });
    }

}

class Program
{
    [DllImport("alut.dll")]
    static extern void alutInit(IntPtr argcp, string[] argv);
    [DllImport("alut.dll")]
    static extern void alutExit();


    static void Main()
    {
        alutInit(IntPtr.Zero, null);

        AudioBuffer buffer = new AudioBuffer(
            System.IO.File.ReadAllBytes("damage1.wav")
            );
        AudioSource source = new AudioSource();
        source.Buffer = buffer.Name;
        source.Play();
        System.Threading.Thread.Sleep(2000);

        alutExit();
    }
}


ここでは、File.ReadAllBytesメソッドを使っていったん"damage1.wav"をメモリ上にbyte[]として読み込んでいます。
それをalutCreateBufferFromFileImageに渡しているのです。

実行結果はあいかわらず「ボガァン」という音です。

拍手[0回]

PR

OpenAL WAVEファイルの再生 [C#]

OpenAL.netのおかげでモチベーションが下がりっぱなしなのですが気を取り直していこうと思います。

alutCreateBufferFromFile
OpenALでは音波のデータを直接再生することが出来ますが、場合によってはこれは低レベルすぎると思う場合があるかもしれません。

たとえば、単に音のファイルを再生したい場合です。

「宇宙船に弾が当たったらdamage1.wavというファイルを再生したい」というような時には、この方法はめんどうです。

しかしありがたいことに、alutはファイルからBufferを作る関数を用意してくれています。
alutCreateBufferFromFileというのがそれで、ファイル名からBufferを作ってその名前を返します。

int alutCreateBufferFromFile (const  char *fileName) ;

失敗した時にはAL_NONE( = 0)を返します。




using System;
using System.Runtime.InteropServices;

class AudioSource
{
    [DllImport("OpenAL32.dll")]
    static extern void alGenSources(int resultSize, int[] result);

    [DllImport("OpenAL32.dll")]
    static extern void alDeleteSources(int nameCount, int[] sourceNames);

    [DllImport("OpenAL32.dll")]
    static extern void alSourcei(int sourceName, int propertyType, int value);
    const int AL_BUFFER = 0x1009;

    [DllImport("OpenAL32.dll")]
    static extern void alSourcePlay(int sourceName);

    private int name;

    public AudioSource()
    {
        int[] names = new int[1];
        alGenSources(names.Length, names);
        this.name = names[0];
    }

    ~AudioSource()
    {
        alDeleteSources(1, new int[] { this.name });
    }

    public int Buffer
    {
        set
        {
            alSourcei(this.name, AL_BUFFER, value);
        }
    }

    public void Play()
    {
        alSourcePlay(this.name);
    }
}

class AudioBuffer
{
    [DllImport("alut.dll")]
    static extern int alutCreateBufferFromFile(string fileName);

    [DllImport("OpenAL32.dll")]
    static extern void alDeleteBuffers(int nameCount, int[] bufferNames);

    private int name;

    public int Name { get { return this.name; } }

    public AudioBuffer(string fileName)
    {
        this.name = alutCreateBufferFromFile(fileName);
    }

    ~AudioBuffer()
    {
        alDeleteBuffers(1, new int[] { name });
    }

}

class Program
{
    [DllImport("alut.dll")]
    static extern void alutInit(IntPtr argcp, string[] argv);
    [DllImport("alut.dll")]
    static extern void alutExit();


    static void Main()
    {
        alutInit(IntPtr.Zero, null);

        AudioBuffer buffer = new AudioBuffer("damage1.wav");
        AudioSource source = new AudioSource();
        source.Buffer = buffer.Name;
        source.Play();
        System.Threading.Thread.Sleep(2000);

        alutExit();
    }
}


このサンプルでは、damage1.wavという、XNAのSpaceWarゲームから拝借したファイル(Content/Audio/Waves/Weapons/damage1.wav)を再生しています。(「ボガァン」という感じの音です。)

拍手[0回]


OpenAL.net

ここ最近、OpenALのマネージト・ラッパーをC#で書いてOpenAL.netとでも名づけようと思っていたのですが、じつはそのものズバリがすでにあるようです。(マヌケすぎます……)

OpenAL.NET

まだソースコードはちらっと見ただけですが、なかなか良い品質のようです。
これでは僕がやることはなにもありませんね……。

拍手[0回]


OpenAL Alutのエラー・ハンドリング [C#]

ALUTでのエラーの扱いについてメモしておきます


ALUTにはエラー関係の関数が2つあるようです。
1つはエラーの定数を取得する関数で、もう1つはその定数から説明をあらわす文字列を取得する関数です。

alutGetError
alutの関数は失敗したときにNULLを返し、どこかにあるグローバル変数にエラー定数をセットするそうです。
で、どのようなエラー定数がセットされたのかを調べる関数がこのalutGetErrorです。

ALenum alutGetError();

戻り値はエラーを表す定数で、以下のものがあるそうです:
ドキュメントを適当に訳しました(本当に適当ですよ)

定数名 説明
ALUT_ERROR_NO_ERROR エラーはありません。 0
ALUT_ERROR_OUT_OF_MEMORY メモリが足りません。 0x200
ALUT_ERROR_INVALID_ENUM Alutの関数に不正な定数が与えられました。 0x201
ALUT_ERROR_INVALID_VALUE Alutの関数に不正な値が与えられました。 0x202
ALUT_ERROR_INVALID_OPERATION 行った操作は現在のALUTの状態では不正です。 0x203
ALUT_ERROR_NO_CURRENT_CONTEXT 現在のコンテキストがセットされていません。(alutInitを呼べば自動的にコンテキストはセットされるのでこれはあまり気にしなくていいでしょう) 0x204
ALUT_ERROR_AL_ERROR_ON_ENTRY ALUT関数へのエントリーにすでにALエラーがあります。
(なんのこっちゃ)
0x205
ALUT_ERROR_ALC_ERROR_ON_ENTRY ALUT関数へのエントリーにすでにALCえらーがあります。 0x206
ALUT_ERROR_OPEN_DEVICE ALCデバイスを開く上でエラーがありました。 0x207
ALUT_ERROR_CLOSE_DEVICE ALCデバイスを閉じる上でエラーがありました。 0x208
ALUT_ERROR_CREATE_CONTEXT Contextを生成する上でエラーがありました。 0x209
ALUT_ERROR_MAKE_CONTEXT_CURRENT 現在のContextを変更できませんでした。 0x20A
ALUT_ERROR_DESTROY_CONTEXT Contextを破壊する上でエラーがありました。 0x20B
ALUT_ERROR_GEN_BUFFERS AL Bufferを生成するのにエラーがありました。 0x20C
ALUT_ERROR_BUFFER_DATA バッファ・データをALに送る途中でエラーがありました。 0x20D
ALUT_ERROR_IO_ERROR I/Oエラー。より詳しくはerrnoを。 0x20E
ALUT_ERROR_UNSUPPORTED_FILE_TYPE サポートされていないファイルタイプです。 0x20F
ALUT_ERROR_UNSUPPORTED_FILE_SUBTYPE ファイルタイプは大丈夫ですが、サポートされていないモードです。 0x210
ALUT_ERROR_CORRUPT_OR_TRUNCATED_DATA サウンドデータに間違いがあるか、欠けています 0x211


alutGetErrorString
alutGetErrorで返された定数はそのままでは人間には理解しがたいため、これをわかりやすい文字列に変換する必要があります。
数値の定数から文字列への変換をやってくれるのが、alutGetErrorString関数です。

const char *alutGetErrorString ( ALenum error);

引数はエラーを表す定数、戻り値はもちろん0で終わる文字列へのポインタです。

using System.Runtime.InteropServices;

enum AlutError
{
    ALUT_ERROR_NO_ERROR = 0,
    ALUT_ERROR_OUT_OF_MEMORY = 0x200,
    ALUT_ERROR_INVALID_ENUM,
    ALUT_ERROR_INVALID_VALUE,
    ALUT_ERROR_INVALID_OPERATION,
    ALUT_ERROR_NO_CURRENT_CONTEXT,
    ALUT_ERROR_AL_ERROR_ON_ENTRY,
    ALUT_ERROR_ALC_ERROR_ON_ENTRY,
    ALUT_ERROR_OPEN_DEVICE,
    ALUT_ERROR_CLOSE_DEVICE,
    ALUT_ERROR_CREATE_CONTEXT,
    ALUT_ERROR_MAKE_CONTEXT_CURRENT,
    ALUT_ERROR_DESTROY_CONTEXT,
    ALUT_ERROR_GEN_BUFFERS,
    ALUT_ERROR_BUFFER_DATA,
    ALUT_ERROR_IO_ERROR,
    ALUT_ERROR_UNSUPPORTED_FILE_TYPE,
    ALUT_ERROR_UNSUPPORTED_FILE_SUBTYPE,
    ALUT_ERROR_CORRUPT_OR_TRUNCATED_DATA
}

class Program
{
    [DllImport("alut")]
    static extern string alutGetErrorString(AlutError error);

    static void Main()
    {
        foreach (AlutError error in System.Enum.GetValues(typeof(AlutError)))
        {
            System.Console.WriteLine(error + " : " + alutGetErrorString(error));
        }

        System.Console.ReadLine();
    }
}


このサンプルを実行すると、定数とその説明をコンソールに表示します。

拍手[0回]


OpenAL 音源とリスナーの位置の変更 [C#]

OpenALではSourceオブジェクト(音源)の3次元空間中の位置を変更して、音の大きさを変えることが出来ます。
リスナー(聞き手)に近づけば音は大きくなりますし、遠ざかれば小さくなります。
遠くの音は小さく、近くの音は大きく聞こえるものです。

デフォルトでは、SourceオブジェクトもListener(インスタンス(?)は一つだけでシングルトンの扱いです。SourceやBufferのような名前(Name)は持っていません。)も位置は{0, 0, 0}だそうです。
これを、OpenALの関数を使って変えることで音の強さによる遠近感が出せるようです。

alSource3f
Sourceオブジェクトの位置を変えるのにはalSource3f関数が使えます。(「使えます」というのは、別の関数、たとえばalSourcefv, alSource3i, alSourceivを使って位置をセットすることも出来るからです。しかし、alSource3fがもっとも使いやすさと柔軟性の面でバランスが取れています。ポインタを使わなくていいですし、位置を表すのにintではなくfloatを使うことが出来るからです。)

void alSource3f(
        int sourceName,
        int propertyType,
        float x, float y, float z
        );


引数はお決まりです。
sourceNameは値をセットするSourceオブジェクトの名前。
propertyTypeはセットする属性を表す定数(ここではAL_POSITION = 0x1004)。
x, y, zはそれぞれ位置をあらわします。

この関数を使うと、位置だけではなく速度(AL_VELOCITY = 0x1006)もセットできます。速度からはドップラー効果を再現できます。

using System;
using System.Runtime.InteropServices;
using System.Threading;

class Program
{
    //OpenAL Utility Toolkit (Alut)の関数をインポート
    [DllImport("alut.dll")]
    static extern bool alutInit(IntPtr argcp, string[] argv);

    [DllImport("alut.dll")]
    static extern bool alutExit();

    [DllImport("alut.dll")]
    static extern int alutCreateBufferHelloWorld();



    //OpenALの関数をインポート
    [DllImport("OpenAL32.dll")]
    static extern void alGenSources(int resultSize, int[] result);

    [DllImport("OpenAL32.dll")]
    static extern void alDeleteSources(int nameCount, int[] sourceNames);

    [DllImport("OpenAL32.dll")]
    static extern void alSourcei(int sourceName, int propertyType, int value);
    const int AL_BUFFER = 0x1009;

    [DllImport("OpenAL32.dll")]
    static extern void alSourcePlay(int sourceName);

    [DllImport("OpenAL32.dll")]
    static extern void alSource3f(
        int sourceName, int propertyType,
        float v1, float v2, float v3
        );
    const int AL_POSITION = 0x1004;


    static void Main()
    {
        alutInit(IntPtr.Zero, null);

        int[] sources = new int[1];
        alGenSources(sources.Length, sources);
        alSourcei(sources[0], AL_BUFFER, alutCreateBufferHelloWorld());

        for (int i = 0; i < 4; i++)
        {
            alSource3f(sources[0], AL_POSITION, 0, 0, -i);
            alSourcePlay(sources[0]);
            Thread.Sleep(1500);
        }

        alDeleteSources(sources.Length, sources);

        alutExit();
    }
}


このサンプルでは、音源をどんどん聞き手から離していっています。
ですから、"Hello, World!" "Hello, World!" "Hello, World!", "Hello, World!"と小さくなるわけです。


alListener3f
ここまでは音源を表すSourceオブジェクトの位置を変えていましたが、リスナー(聞き手)の位置を変えることも出来ます。
聞き手が音から離れていっても音は小さくなるでしょうからね。

リスナーの位置を変えるには、alListener3f関数が使えます。(やはりalListener3ialListeneriv, alListenerfvみたいな別の関数も使えますが、位置のセットにはこれが一番適しています)

void alListener3f(int propertyType, float x, float y, float z);

この関数はalSource3fにとてもよく似ていますが、ただひとつ、第一引数にオブジェクトを表す名前がありません。
Listenerはシングルトンなので名前は必要ないのです。

その他の引数は全く同じです。
propertyTypeは設定する属性を表す定数(位置をセットしたいのならSourceのときと同じようにAL_POSITION)、
x, y, zは位置の成分です。


using System;
using System.Runtime.InteropServices;
using System.Threading;

class Program
{
    //OpenAL Utility Toolkit (Alut)の関数をインポート
    [DllImport("alut.dll")]
    static extern bool alutInit(IntPtr argcp, string[] argv);

    [DllImport("alut.dll")]
    static extern bool alutExit();

    [DllImport("alut.dll")]
    static extern int alutCreateBufferHelloWorld();



    //OpenALの関数をインポート
    [DllImport("OpenAL32.dll")]
    static extern void alGenSources(int resultSize, int[] result);

    [DllImport("OpenAL32.dll")]
    static extern void alDeleteSources(int nameCount, int[] sourceNames);

    [DllImport("OpenAL32.dll")]
    static extern void alSourcei(int sourceName, int propertyType, int value);
    const int AL_BUFFER = 0x1009;

    [DllImport("OpenAL32.dll")]
    static extern void alSourcePlay(int sourceName);

    const int AL_POSITION = 0x1004;

    [DllImport("OpenAL32.dll")]
    static extern void alListener3f(int propertyType, float x, float y, float z);


    static void Main()
    {
        alutInit(IntPtr.Zero, null);

        int[] sources = new int[1];
        alGenSources(sources.Length, sources);
        alSourcei(sources[0], AL_BUFFER, alutCreateBufferHelloWorld());

        for (int i = 0; i < 4; i++)
        {
            alListener3f(AL_POSITION, 0, 0, i);
            alSourcePlay(sources[0]);
            Thread.Sleep(1500);
        }

        alDeleteSources(sources.Length, sources);

        alutExit();
    }
}


実行結果は先ほどと同じになるはずです。
ただ違うのは、このサンプルでは音源ではなく聞き手のほうを動かしているということです。















拍手[1回]