忍者ブログ

Memeplexes

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

OpenALで音を生成 [C#]

プログラムから音の波のデータを作って、それを再生してみます。
(実はこれをやりたくて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)

このフラグを、2つ目の引数formatに入れます。

最初の引数bufferNameはもちろんデータをセットするBufferオブジェクトの名前です。C#でラッパークラスを作ったなら、間違いなく(引数ではなく)メンバ変数になるでしょうね。

3番目の引数dataは音の波データです。具体的にこれがどのようなデータであるかは、引数formatによります。8bitを選んだのならbyte[]になるでしょうし、16bitを選んだらUInt16[]になるでしょう。ステレオを指定した場合は、左のチャンネルが先に来て、後に右のチャンネルが来ます(わかりにくい表現ですが、このようなことがドキュメントに書いてあります)。

4番目の引数sizeはdataのサイズですが、気をつけなければいけないのはこれはbyte単位だということです。配列の長さではありません。

5番目の引数frequency(日本語では周波数)は、波データの密度のようですね。1秒間にいくつdataの中の要素を使うかを表しているようです(ドキュメントには説明がありません・・・)。たとえばこれが20000なら一秒間に2万個の要素を使って音を出します。dataの長さが10000なら0.5秒で再生し終わるでしょう。(たぶん)

alDeleteBuffers
使い終わったBufferオブジェクトを閉じるにはalDeleteBuffers関数を使います。

void alDeleteBuffers(ALsizei n, const ALuint *bufferNames);

これを呼んだら、そのBufferの名前は使えなくなります。

nは配列bufferNamesの長さで、bufferNamesはデリートするBufferオブジェクトが入っている配列です。

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;     } }


これを実行すると、3秒間周波数440の”ラ”の音が聞こえるはずです。
波の形は単純なサイン・カーブなので「プー」という無機質な音です。
これに音色を加えるには、(周波数はそのままで)波の形を変えてやればいいそうです。


※訂正:たぶんこれはサイン波ではありません。サイン波を生み出す正しい方法はこちら









 

拍手[0回]

PR