ALSA環境の構築して Linux上で音楽を鳴らす

Linuxの音声再生環境を整える

ALSA (Advanced Linux Sound Architechture) は Linux用のサウンドAPI群です

1999年頃 前身である OSS(Open Sound System)が普及するまでは
サウンドボード毎にドライバも異なり再生環境を構築するのは特殊な作業でした

アプリケーションが OSSに対応し始めると一気に Linuxのサウンド環境が改善し
今まで「無音状態」だった Linuxシステムにマルチメディアという魅力が付与されたのです

Linux-2.6では ALSAがカーネルに取り込まれ標準機能となると
Windowsと同じように「何も意識しなくても再生環境は整っている」状況までなりました
ALSAが一気に普及できたのは OSSよりも高機能な上
OSS互換APIを持っていたため アプリケーションの移行がスムーズに進んだためです

ここでは 下記の検証環境で ALSAの環境構築までをレポートします

検証環境
CPU AMD Turion-MT37 2GHz
メモリ DDR-400 1.5GB
サウンド SoundBlaster Live! Value
OS Linux-2.6.39
GCC gcc-4.6.2
alsa-lib alsa-lib-1.0.25
alsa-utils alsa-utils-1.0.25
awesfx awesfx-0.5.1d

ALSAパッケージ構成とインストール

ALSA関連のパッケージは複数に別れています
ALSA公式ページ で配布されている各パッケージの役割を整理すると

パッケージ

alsa-firmware

特定のサウンドボード向けファームウェア
alsa-driver

ドライバ本体 Linux-2.6以降は不要
alsa-oss

liboss ALSA自体が既にOSSエミュレーションするので通常不要
alsa-plugins

追加プラグイン 通常不要
alsa-lib

基本ライブラリ libasound 必要
alsa-utils

alsamixer や aplay などの基本アプリ

Linux-2.6以上であれば alsa-lib と alsa-utils のみで十分です

alsa-lib は下記のオプションでインストールしました

$ ./configure --prefix=/usr/local
checking build system type... i686-pc-linux-gnu
checking host system type... i686-pc-linux-gnu
checking for a BSD-compatible install... /bin/install -c

〜

config.status: creating include/config.h
config.status: executing depfiles commands
Creating asoundlib.h...
$ make
〜
$ su
〜
# make install
〜
#
 

aserverコマンドや libasoundライブラリがインストールされます

alsa-utils は下記オプションでインストールしました

$ ./configure --prefix=/usr/local --disable-xmlto --with-udev-rules-dir=/etc/udev/rules.d --with-asound-state-dir=/var/state
checking for a BSD-compatible install... /bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /bin/mkdir -p

〜

config.status: creating po/POTFILES
config.status: creating po/Makefile
config.status: executing depfiles commands
$ make
〜
$ su
〜
# make install
〜
#
 

こちらは基本ツールがメインで bin/ 以下に下記のツールがインストールされます

aconnect  alsamixer  amidi   aplay      arecord      aseqdump  iecset
alsaloop  alsaucm    amixer  aplaymidi  arecordmidi  aseqnet   speaker-test
 

音楽再生 MIDI再生 録音 設定ツール が一通り揃っています
speaker-test で音声が鳴るのを確認したら ALSA関連のインストールは完了です
後は mpg123など libasound対応のアプリを好きなだけインストールしましょう

$ speaker-test

speaker-test 1.0.25

Playback device is default
Stream parameters are 48000Hz, S16_LE, 1 channels
Using 16 octaves of pink noise
Rate set to 48000Hz (requested 48000Hz)
Buffer size range from 128 to 32768
Period size range from 32 to 32768
Using max buffer size 32768
Periods = 4
was set period_size = 8192
was set buffer_size = 32768
 0 - Front Left
^C
$
 

もし SoundBlasterシリーズの音源カードを利用されているなら
MIDI再生の環境も整えてしまいましょう

ほとんどの SoundBlasterシリーズのカードは「サウンドフォント」をサポートしていて
ハードウェアでMIDIを鳴らすことができます
最近はゲーム等でも MIDIを鳴らす機会が減ってしまいましたが
SoundBlasterシリーズの魅力の一つとして このサウンドフォントサポートが挙げられます

ここでは サウンドフォントをシステムに読み込むための asfxloadをインストールして
実際にサンプルMIDIを演奏するところまでを紹介します
asfxloadは awesfxパッケージに含まれています
ソースコードを展開して configure 〜 make 〜 make install します

$ ./configure --prefix=/usr/local --with-sfpath=/usr/local/share/sounds/sf2
checking for a BSD-compatible install... /bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /bin/mkdir -p

〜

config.status: creating etc/Makefile
config.status: creating include/config.h
config.status: executing depfiles commands
$ make
Making all in awelib

〜

gcc -g -O2 -o sfxtest sfxtest.o seq.o  awelib/libawe.a /usr/local/lib/libasound.so -lrt -lm -ldl -lpthread   -Wl,--rpath -Wl,/usr/local/lib -Wl,--rpath -Wl,/usr/local/lib
make[1]: Leaving directory `/home/deer/admin/Alsa/awesfx-0.5.1d'
$ su
# make install
〜
#
 

configureではサウンドフォント配置先を /usr/local/share/sounds/sf2/ としました
インストールされるコマンドは 下記のとおりです

asfxload  aweset  gusload  setfx  sf2text  sfxload  sfxtest  text2sf
 

サウンドフォントは SoundBlasterのドライバCDなどに含まれている
8mbgmsfx.sf2 を使いました ファイル名の通りGM準拠で8MBの容量があります
サウンドフォントディレクトリに 8mbgmsfx.sf2 をコピーして asfxloadでシステムにロードします

# ls /usr/local/share/sounds/sf2/
8mbgmsfx.sf2  default-2m.bnk    test.bnk       xgmap.bnk
README-bank   emu8m.bnk         xgdefault.bnk  xgsfx.bnk
ch12msup.bnk  setfx-sample.cfg  xgdrum.bnk
# cat /proc/asound/Live/wavetableD1
Device: Emu10k1
Ports: 4
Addresses: 17:0 17:1 17:2 17:3
Use Counter: 0
Max Voices: 64
Allocated Voices: 0
Memory Size: 134217728
Memory Available: 134213632
Allocated Blocks: 1
SoundFonts: 0
Instruments: 0
Samples: 0
Locked Instruments: 0
Locked Samples: 0
# asfxload /usr/local/share/sounds/sf2/8mbgmsfx.sf2
# cat /proc/asound/Live/wavetableD1
Device: Emu10k1
Ports: 4
Addresses: 17:0 17:1 17:2 17:3
Use Counter: 0
Max Voices: 64
Allocated Voices: 0
Memory Size: 134217728
Memory Available: 126786844
Allocated Blocks: 527
SoundFonts: 1
Instruments: 1849
Samples: 526
Locked Instruments: 1849
Locked Samples: 526
#
 

SoundFontsの項がカウントアップされているのがわかります
(サウンドフォントはメモリの許す限り複数ロードできますが 検証環境では試してません)
また MIDI用のポートが 17:0 17:1 17:2 17:3 と 4つあることもわかります
ではサンプルとしてどこからか拾ってきた ワルキューレの伝説 を鳴らしましょう

$ aplaymidi -p 17:0 LEGENDofVALKYRIE.mid
$
 

サウンドが鳴れば 無事にMIDI環境が動作しています
asfload のロードはシステム起動時に実行する必要があるため 起動スクリプト等に組込んで完成です

8bit 16bit 時代では高価な MIDI専用機を購入しなければ手に入らなかった
MIDI再生環境が 今やLinux上で再現できてしまいます 感慨深いものです
MIDI Roland SD-20
一方 ハードウェア音源の例では 上記 Roland SD-20 のようなものが挙げられます
最近の MIDI再生機器も小型化され USB接続可能となるなどの進化を遂げており
(通常 MIDI機器は専用のMIDIインターフェースで音源ボードと接続する)
実際筆者はこの SD-20を所持していて クリアな発声などそれなりの魅力もありますが
やはり ハードウェア音源はお金持ち向けという印象がぬぐえません

ちなみに Windows+DirectXは ソフトウェア音源上で MIDIを演奏する動作になってます
従って安物のオンボードサウンドチップでも 何も考えずにMIDIが鳴ります
(MIDI再生に必要なリソースは CPUが負担しているため
SoundBlasterご利用なら MIDI出力をサウンドフォント用ポートに切り替えたほうがよいでしょう)


トラブル事例

筆者の検証環境のみの特殊事例かもしれませんが 当初下記エラーが出て困っていました

$ aplay -L
Segmentation fault
$
 

で全く音声が鳴らないという事象です
原因を調査した結果 「-lライブラリ にて実行時リンクするライブラリに対して 動的リンクも行い長いシンボル名を dlopen() dlsym() dlclose() 繰り返すと不正終了する」
といった 日本語で表現するのも難しいほど 理解不能な現象でした

下記検証プログラム t_main.c で Segmentation fault が再現されました

#include 
#include 

int main ()
{
  int i;
  void *handle = NULL;
  void *target = NULL;

  for ( i = 0; i != 10; i++ ) {
    handle = dlopen ( "libz.so", RTLD_NOW );
    if ( handle )
      target = dlsym ( handle, "long___________long" );
    else
      printf ( "failed dlopen()\n" );
    dlclose ( handle );
    printf ( "count=%d\n", i );
  }
  return ( 0 );
}
 

/usr/local/lib/libz.so を実行時リンクする下記のバイナリを作って実行すると

$ gcc -lz -ldl -o t_main t_main.c
$ ./t_main
count=0
count=1
Segmentation fault
$
 

カーネルの問題なのか glibcの問題なのか判りません 検証環境のみの現象かもしれません
alisa-lib が丁度この条件に引っかかったようでしたので
src/dlmisc.c の snd_dlopen() 関数部分を修正して対策しました

 51 void *snd_dlopen(const char *name, int mode)
 52 {
 53         return dlopen(name, mode);
 54 #ifndef PIC
 55         if (name == NULL)
 56                 return &snd_dlsym_start;
 57 #else
 58 #ifdef HAVE_LIBDL
 59         if (name == NULL) {
 60                 static const char * self = NULL;
 61                 if (self == NULL) {
 62                         Dl_info dlinfo;
 63                         if (dladdr(snd_dlopen, &dlinfo) > 0)
 64                                 self = dlinfo.dli_fname;
 65                 }
 66                 name = self;
 67         }
 68 #endif
 69 #endif
 70 #ifdef HAVE_LIBDL
 71         return dlopen(name, mode);
 72 #else
 73         return NULL;
 74 #endif
 75 }
 

53行目にあるように いきなり return dlopen(name, mode); で返るように修正しました
name= にあたる部分の /usr/local/lib/libasound.so.2 が長い名前だったことが
今回の不具合に該当してしまったようです

$ aplay -L
null
    Discard all samples (playback) or generate zero samples (capture)
default:CARD=Live
    SB Live! Value [CT4670], ADC Capture/Standard PCM Playback
    Default Audio Device
sysdefault:CARD=Live
    SB Live! Value [CT4670], ADC Capture/Standard PCM Playback
    Default Audio Device
front:CARD=Live,DEV=0
    SB Live! Value [CT4670], ADC Capture/Standard PCM Playback
    Front speakers
rear:CARD=Live,DEV=0
    SB Live! Value [CT4670], ADC Capture/Standard PCM Playback
    Rear speakers
center_lfe:CARD=Live,DEV=0
    SB Live! Value [CT4670], ADC Capture/Standard PCM Playback
    Center and Subwoofer speakers
surround40:CARD=Live,DEV=0
    SB Live! Value [CT4670], ADC Capture/Standard PCM Playback
    4.0 Surround output to Front and Rear speakers
surround41:CARD=Live,DEV=0
    SB Live! Value [CT4670], ADC Capture/Standard PCM Playback
    4.1 Surround output to Front, Rear and Subwoofer speakers
surround50:CARD=Live,DEV=0
    SB Live! Value [CT4670], ADC Capture/Standard PCM Playback
    5.0 Surround output to Front, Center and Rear speakers
surround51:CARD=Live,DEV=0
    SB Live! Value [CT4670], ADC Capture/Standard PCM Playback
    5.1 Surround output to Front, Center, Rear and Subwoofer speakers
iec958:CARD=Live,DEV=0
    SB Live! Value [CT4670], Multichannel Capture/PT Playback
    IEC958 (S/PDIF) Digital Audio Output
$
 

aplay -L も正しく結果を返すようになり 音楽も鳴りました