ソフトウェア設計及び演習2017

音を鳴らす

SDL(Simple DirectMedia Layer)

  • 非常にシンプルなAPI
    • 厳選された機能を,高性能で提供することを目指した,らしい
  • あまり使いやすいものではない,という評価が多く,現在では別のライブラリが利用されることも多い

チュートリアル

コンパイル,リンク時の注意

SDL,SDL_mixer を使ったプログラムには

#include <SDL.h>
#include <SDL_mixer.h>

が必要です.また,コンパイル時には

$ gcc -o sound sound.c `sdl-config --cflags --libs` -lSDL_mixer

のように,sdl-config と -lSDL_mixer の指定が必要です.

初期化と終了

SDL_mixer の初期化と終了はそれぞれ

です.

Mix_OpenAudio(int frequency, Uint16 format, int channels, int chunksize)
frequency:サンプリング周波数(Hz)を指定します.特に気にしない場合はMIX_DEFAULT_FREQUENCYでOK.
format:波形サンプルの型(8ビット,16ビット,など)を指定します.これも通常はMIX_DEFAULT_FORMATでOK.
channels:チャンネル数を指定します.1ならモノラル,2ならステレオです.
chunksize:サウンドバッファのサイズをバイト数で指定します.あまり気にする必要はないので,1024あたりでOK.

BGMを鳴らす

Mix_LoadMUS()で曲をロードし,Mix_PlayMusic()で再生するだけです.

Mix_Music  *music;

music = Mix_LoadMUS( "hoge.mp3" );  // ファイルから読み込み

Mix_PlayMusic( music, -1 );         // 2番目の引数は演奏回数.
                                    // -1なら無限に繰り返す.

BGMを止めたいときは,Mix_HaltMusic()で演奏を停止し,Mix_FreeMusic()で曲を解放します.

Mix_HaltMusic();       // 演奏停止
Mix_FreeMusic(music);  // 曲を解放

効果音を鳴らす

BGMの再生とほとんど同じですが,効果音は同時に複数鳴らすことがあるため,再生時にチャンネル番号を指定する必要があります.

1つのチャンネルでは,同時に1つの音しか再生できません.つまり,同時に複数の音を鳴らすためには複数のチャンネルを使用する必要があります.

また,再生停止するときも「チャンネル○番を停止」というように,チャンネル番号を指定する必要があります.

具体的なコードは以下です.Mix_LoadWAV()で効果音をロードし,Mix_PlayChannel()で再生します.

Mix_Chunk *chunk;
int       channel;

chunk = Mix_LoadWAV( "hoge.wav" );          // wavファイルを読み込む

channel = Mix_PlayChannel( -1, chunk, 0 );  // 再生
            /*
             * ● 1番目の引数はチャンネル番号の指定
             *    -1にすると空チャンネルが自動的に割り当てられる
             * ● 3番目の引数は再生繰り返し数の指定
             *    0なら1回再生,1なら2回再生,...となり,-1なら無限に繰り返す
             * ● 戻り値は割り当てられたチャンネル番号
             */

後始末にはMix_HaltChannel()Mix_FreeChunk()を使用します.

Mix_HaltChannel( channel );  // チャンネル番号channelの再生停止
                             // -1を与えると全チャンネル停止

Mix_FreeChunk( chunk );      // 音の解放

SDLを用いたサンプル

https://svn.cis.iwate-u.ac.jp/svn/csd/kimura/sound/sdl_sample

  • チェックアウトして,(cd sdl_sample; make; ./sdl_sample) で実行できます.
  • BGMがエンドレスで流れます.著作権フリー素材を拝借しています.
  • 効果音1,効果音2をクリックすると,それぞれ対応する音が(BGMに重なって)鳴ります.
  • Quitボタンをクリックすると,3秒かけてフェードアウトし,その後終了します.

ここより下の項目は書きかけです!

OpenAL (Open Audio Library)

  • クロスプラットフォームのオーディオAPI
  • マルチチャンネル3次元定位オーディオを効率よく表現するように設計されている
  • APIのスタイルと慣習は意図的にOpenGLと似せてある

Hello, Worldを喋らせる

#include <AL/alut.h>

int main(int argc, char** argv)
{
  ALuint buffer, source;

  // 初期化
  alutInit(&argc,argv);

  // 有効なバッファとソース番号を取得
  alGenBuffers( 1, &buffer );
  alGenSources( 1, &source );

  // 提供されている"hello world"の音声バッファを作成
  buffer = alutCreateBufferHelloWorld ();

  // ソースとバッファを結び付ける
  alSourcei( source, AL_BUFFER, buffer );

  // 再生
  alSourcePlay( source );

  // 1秒待って,終了処理
  alutSleep (1);
  alutExit();

  return 0;
}

コンパイル,実行

$ gcc -o testal testal.c -lopenal -lalut
$ ./testal

wavファイルを鳴らす

先のサンプルで,バッファに読み込む部分でwavファイルを指定すればOK.

例えば,se.wavを鳴らしたければ

buffer = alutCreateBufferFromFile( "se.wav" );

と変更するだけです.

sin波の作成と再生

sin波を作って再生するサンプル.

#include <math.h>
#include <AL/alut.h>

int main(int argc, char** argv)
{
  ALuint buffer, source;

  alutInit(&argc,argv);

  alGenBuffers( 1, &buffer );
  alGenSources( 1, &source );

  // サンプリング周波数と音階の設定
  const int freq = 10000 , Hz = 440;

  // sin波データ作成
  ALshort data[freq/Hz];
  for (int i = 0; i < freq/Hz ; ++i) {
    data[i] = 32767 * sin(i * 3.14159 * 2 * Hz / freq);
  }

  // バッファに音データを渡す
  alBufferData( buffer, AL_FORMAT_MONO16, data, freq/Hz*sizeof(ALshort), freq );

  alSourcei( source, AL_BUFFER, buffer );
  alSourcei( source, AL_LOOPING, AL_TRUE );

  alSourcePlay( source );

  alutSleep (1);

  alutExit();
  return 0;
}
  • 上記プログラムをコンパイルするには,-std=c99 オプションが必要です.
  • const int freq = 10000 , Hz = 440;で,サンプリング周波数を10000Hz,音階を440Hz(「ラ」)としています.
    • もちろん,音階の数値を変えれば音が変わります.
  • for文でデータを作っています.
  • alBufferData( buffer, AL_FORMAT_MONO16, data, freq/Hz*sizeof(ALshort), freq );
    • 第2引数のAL_FORMAT_MONO16は,モノラル16bit-PCM音源であることを示しています.
    • 第4引数は渡すデータの長さ(byte単位),第5引数はサンプリング周波数です.
  • このプログラムでは1波長分しか音データを作成していないので,alSourcei( source, AL_LOOPING, AL_TRUE );を呼んで,ループ再生をオンにしています.
    • alutSleep()の時間を長くすれば,その分,鳴り続けます.

ちなみに,

  • AL_FORMAT_MONO8は,8bit-PCM音源,0~255の値
  • AL_FORMAT_STEREO8は,ステレオの8bit-PCM音源
  • AL_FORMAT_STEREO16は,ステレオ16bit-PCM音源,データは左右左右・・・と交互に並べます

BGMとして流したい

以下未確認.

  • alSourcei(source,AL_LOOPING,AL_TRUE) で繰り返しONにしておき,
  • 何かのイベントをトリガーにして alSourcePlay(source) を呼び出して再生スタート,
  • 何かのイベントをトリガーにして alSourceStop(source) で再生ストップ,
  • alutExit()が呼ばれた場合は無条件停止?

のような形にすれば実現可能?

OpenALを使う場合の設定

端末で使うためには,

  • libopenal-dev
  • libalut-dev

のインストールが必要.(2013/10/24時点で未インストール)


最終更新日:2017/04/03 09:14:25