[PR]
[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
プログラミング、3DCGとその他いろいろについて
[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
| 定数名 | 説明 | 値 |
| 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 |
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();
}
}
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();
}
}
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();
}
}
プログラムから音の波のデータを作って、それを再生してみます。
(実はこれをやりたくてOpenALをはじめたんです!)
音は波ですから、波の高さをByteか何かの配列として表し、それを再生することが出来るわけです。
OpenALでは、8bitと16bitのデータの配列を使えるようなので、使うとしたらSystem.ByteかSysytem.UInt16の配列でしょうね。
alGenBuffers
前回と違うのは、音のデータを表すオブジェクト、Bufferをまず自分で作らなければいけないということです。(そしてそのBufferオブジェクトに波のデータ、byte[]をセットします。) Bufferオブジェクトを作るには、alGenBuffers関数を使います。
void alGenBuffers (ALsizei n, ALuint *bufferNames);
これはSourceオブジェクトを生成するalGenSources関数にとても似ています。実際、使い方は同じです。この関数は一度に複数のBufferオブジェクトを作ることが出来ます。引数のnは生成するBufferオブジェクトの数で、bufferNamesに生成されたBufferオブジェクト(の名前:Name)が格納されます。
alBufferData
こうして生成したBufferオブジェクトに音の波の高さを表すbyteかUInt16の配列をセットするには、alBufferData関数を使います。引数がたくさんありますが、C#でラッパークラスを作れば2つまで絞り込めます。
void alBufferData(
ALuint bufferName,
ALenum format,
const ALvoid *data
ALsizei size,
ALsizei frequency
);
この関数はいろいろなタイプの波のデータをセットすることが出来ます(全部で4つ)。モノラルかステレオかという選択と、8-bitか16-bitかという選択で、2 x 2 = 4とおりです。
| モノラル | ステレオ | |
| 8-bit | AL_FORMAT_MONO8 (0x1100) |
AL_FORMAT_STEREO8 (0x1102) |
| 16-bit | AL_FORMAT_MONO16 (0x1101) |
AL_FORMAT_STEREO16 (0x1103) |
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(); //OpenALの関数をインポート //Source関連 [DllImport("OpenAL32.dll")] static extern void alGenSources(int resultSize, int[] result); [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 alDeleteSources(int nameCount, int[] sourceNames); //Buffer関連 [DllImport("OpenAL32.dll")] static extern void alGenBuffers(int resultSize, int[] result); [DllImport("OpenAL32.dll")] static extern void alBufferData( int bufferName, int soundDataFormat, byte[] data, int size, int samplePerSecond ); const int AL_FORMAT_MONO8 = 0x1100; [DllImport("OpenAL32.dll")] static extern void alDeleteBuffers(int nameCount, int[] bufferNames); static void Main() { alutInit(IntPtr.Zero, null); //Bufferオブジェクト(音のデータ)の初期化 int[] buffers = new int[1]; alGenBuffers(buffers.Length, buffers); byte[] waveData = createAWave(22000); alBufferData(buffers[0], AL_FORMAT_MONO8, waveData, waveData.Length, 22000); //Sourceオブジェクト(音源)の初期化 int[] sources = new int[1]; alGenSources(sources.Length, sources); alSourcei(sources[0], AL_BUFFER, buffers[0]); //再生 alSourcePlay(sources[0]); Thread.Sleep(3000); alDeleteBuffers(buffers.Length, buffers); alDeleteSources(sources.Length, sources); alutExit(); } //3秒間の"ラ"の音を作る //"ラ"の周波数:440ヘルツ static byte[] createAWave(int samplePerSecond) { byte[] soundData = new byte[samplePerSecond * 3]; for (int i = 0; i < soundData.Length; i++) { soundData[i] = (byte)( 255 * Math.Sin((2 * Math.PI * 440) * i / samplePerSecond) ); } return soundData; } }
前回の補足として、OpenALの関数の解説をしておきますね。
ALUTの関数
(alut.dll)
alutInit
OpenALをより簡単に使うためのライブラリとしてALUT(OpenAL Utility Library)というのがあるのですが、それを初期化するのにalutInit関数というのを使います。
ALboolean alutInit ( int *argcp, char **argv);
これを呼ぶとALUTライブラリを初期化してALUTが使えるようになります。
引数は何を意味しているのかというと……コマンドラインの引数のようですね。
最初のargcpはたぶんargvの数を表す変数へのポインタで、argvはコマンドラインの引数そのもののようです。
ドキュメントには用途が詳しく書いていないのでどう使えばいいのかよくわかりませんが、この両方にNULLを渡してもかまわないそうです。(何のためにあるんだろ・・・)
戻り値は成功したならtrue、失敗したならfalseです。
alutExit
また、ALUTライブラリを使って音を出した後、アプリケーション終了前に後始末としてalutExit関数を呼ばなければいけません。(もちろん、この関数を呼ばずにアプリケーションを終了して、クリーンアップはOSに任せるというのも一つの手です)
ALboolean alutExit();
この関数を呼んだあと、またalutInitを使ってALUT初期化するのもアリだそうです。
戻り値は成功したならtrue、失敗したならfalseです。
alutCreateBufferHelloWorld
ALUTはまた、初学者のために"Hello, World!"という音声データをあらかじめ用意してくれています。
この音声データを取得するにはalutCreateBufferHelloWorld関数を使います。
ALuint alutCreateBufferHelloWorld();
Bufferというのは音声データを表すオブジェクトです。これを音源を表すSourceオブジェクトにセットし、再生することでスピーカーから音を出します。Bufferは純粋な音声データだけであるのに対し、Sourceは音源の位置、速度、向きなどから物理法則にしたがって音声データを加工する役割を持っているようです。Sourceが遠くにあれば音は小さくなりますし、こちらへ近づいてきているのならドップラー効果で音が高くなります。
戻り値は"Hello, World!"という音声データのBufferを表すハンドルのようなもの(整数)です。(ただし、OpenALではこれをハンドルではなくNameと表現するようですね。今後はNameという用語を使うことにします)
OpenALの関数
OpenAL
突然ですがOpenALなるものでHello Worldをしてみました。
OpenALというのはOpenGLのオーディオ版のようなものなのだそうです。
(Open Audio Library)
OpenGLがグラフィックス・ライブラリ(GL)なのにたいし、OpenALはオーディオ・ライブラリ(AL)というわけです。
ではなぜこのようなことをするのかというと、4gamer.netのこの記事を読んだからです。
これを読むと、なんとなくOpenALが出来たほうがいいような気がしてきます。
もちろん、無駄骨になる可能性もあります。
Microsoft.Xna.Framework.Audioがあるから知る必要は無いのではないかという気もします。
でもこっちの方が柔軟そうですし、好奇心がこれをやりたいと言っているのです。
というわけで、C#のInteropサービスを利用してOpenALを使ってみることにしました。
最初はC++/CLIでやろうと思っていたのですが、挫折しました(笑)。
全部C#で行きます。
インストール
OpenALのインストールは、まずここの次の2つをダウンロードします。
OpenAL 1.1 Installer for Windows
freealut Binary ZIP
上のほうは実行、下は展開してlibの中にあるalut.dllをC#で作る実行ファイルと一緒に置いてやります(Visual Studioを使う場合は単にプロジェクトに追加+右クリック > プロパティ > でアウトプットディレクトリにコピーするように設定すればいいでしょう)。
Hello World
では単純にスピーカーから”Hello World”と鳴らすプログラムを書いてみましょう。
using System;
using System.Runtime.InteropServices;
class Program
{
//OpenAL Utility Toolkit (Alut) の関数をインポート
const string AlutDll = "alut.dll";
[DllImport(AlutDll)]
static extern bool alutInit(IntPtr argc, string[] argv);
[DllImport(AlutDll)]
static extern bool alutExit();
[DllImport(AlutDll)]
static extern int alutCreateBufferHelloWorld();
//OpenALの関数をインポート
const string OpenALDll = "OpenAL32.dll";
[DllImport(OpenALDll)]
static extern void alSourcePlay(int sourceHandle);
[DllImport(OpenALDll)]
static extern void alGenSources(int resultSize, int[] result);
const int AL_BUFFER = 0x1009;
[DllImport(OpenALDll)]
static extern void alSourcei(int sourceHandle, int propertyType, int value);
static void Main()
{
alutInit(IntPtr.Zero, null);
//"Hello World!"と喋る音声データを作る
int bufferHandle = alutCreateBufferHelloWorld();
//音源を作り、音声データをセットする
int[] sources = new int[1];
alGenSources(sources.Length, sources);
alSourcei(sources[0], AL_BUFFER, bufferHandle);
//音源から音を鳴らす
alSourcePlay(sources[0]);
System.Threading.Thread.Sleep(1300);
alutExit();
}
}