LXCで学ぶコンテナ入門 -軽量仮想化環境を実現する技術

第15回 LXCの構築・活用 [3] ― コンテナ内でサウンドを取り扱う

この記事を読むのに必要な時間:およそ 6 分

この連載のLXCの構築・活用のシリーズが,今回で3回目を迎えました。目的に合わせたパッケージ構成で,システムコンテナを作成・構築することに,だいぶ慣れてきたかと思います。皆さまは,作成・構築したコンテナをどのように利用されているでしょうか。

LXCでコンテナを使うときは,たいていサーバ用途のアプリケーションを導入して運用することが多く,これまでコンテナ内でサウンドを使う手法は,あまり知られていませんでした。しかし,コンテナ上でサウンドを使うアプリケーションの利用や評価を行ったり,多数のコンテナを起動して,利用者向けにリモートコンソール環境やリモートデスクトップ環境を提供するなど,サウンドを必要とする場面も考えられ,このような使い方に興味をお持ちの方も多いのではないかと思います。今回は,コンテナ内でサウンドを取り扱うにはどうすれば良いのかを紹介していきます。

Linuxにおける主なサウンドシステム・サウンドサーバ

まず,Linuxにおける主なサウンドシステム・サウンドサーバについて,簡単に整理しておきましょう。

Open Sound System(OSS)
UNIXベースのプラットフォームで使うことができるサウンドシステムで,ハードウェアを直接制御するOSSドライバを含んでいます。
Advanced Linux Sound Architecture(ALSA)
OSSを置き換えるために開発されたLinuxにおける高機能なサウンドシステムで,現在はALSAが主流となっています。ALSAドライバとOSSエミュレーション機能を含んでいます。
Enlightened Sound Daemon(ESD,EsounD)
GNOMEで使われていたサウンドサーバで,実行中の各種アプリケーションが出力するサウンドを取りまとめて,ローカルデバイスやネットワーク先のデバイスに送り出します。
PulseAudio
ESDの置き換えとして,クロスプラットフォームでネットワークに対応したサウンドサーバで,現在はPulseAudioが主流となっています。ESDエミュレーション機能を含んでいます。

これらのサウンドシステム・サウンドサーバの関係は,図1のようになっています。基本的に,OSS APIを使用したアプリケーションは/dev/dsp/dev/audioなど(OSS互換デバイスファイル)を通じてデバイスにアクセスし,ALSA APIを使用したアプリケーションは/dev/snd/*(ALSAデバイスファイル)を通じてデバイスにアクセスします。Plamo Linuxでは,aplayコマンドなどのALSA APIを使用したアプリケーションやesdplayコマンドなどのESD APIを使用したアプリケーションは,paplayコマンドなどのPulseAudio APIを使用したアプリケーションとともに,PulseAudioを経由して処理されるように設定されており,またOSS APIを使用したアプリケーションについても,padspコマンド(ラッパープログラム)使って,PulseAudioで一元的に管理することができます。

図1 サウンドシステム・サウンドサーバの関係

図1 サウンドシステム・サウンドサーバの関係

OSSを経由してサウンドを出力する

まず,ホスト上で音を出すことを考えてみましょう。OSS互換オーディオデバイスファイルを経由してサウンドを出力する仕組みはとても簡単です。Sunオーディオファイルであれば,以下のように/dev/audioにリダイレクトすることにより,音を出すことができます。

taro@host:~$ cat pipipipi.au > /dev/audio

Sunオーディオファイルは,サン・マイクロシステムズが策定した音声ファイル形式です。8ビットのμ-lawで符号化された形式(実際の量子化ビット数は14ビット相当)で,サンプリング周波数は8kHzとなっています。人間の聴覚が,対数的に音の大きさを知覚する性質を利用して,信号を対数的に圧縮することにより,実用的なダイナミックレンジを稼いでいます。上の例で使っているSunオーディオファイルは,Plamo Linuxをフルインストールすると,/usr/lib/X11/xmascot/pipipipi.au(x86版の場合)⁠もしくは/usr/lib64/X11/xmascot/pipipipi.au(x86_64版の場合)に置かれています。

一般的なWAVファイルのような,Sunオーディオファイル以外の音声ファイルについては,/dev/dspを使います。このとき,音声ファイルをそのままリダイレクトするのではなく,/dev/dspをオープンしてから音声ファイル形式を設定し,そこに音声データを流し込むことにより,音を出ことができます。

ここで,16ビット/ステレオ/44.1kHzの音声ファイルを再生するサンプルプログラムdsptest.cを紹介します。わかりやすさ優先で,ファイル名は決め打ち,エラー処理は必要最小限に止めた簡単なプログラムです。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/soundcard.h>
#include <sys/ioctl.h>
#include <unistd.h>

static unsigned char buf[2048];

int main(void)
{
    int fdi, fdo, arg, len;

    fdi = open("geragerapo-no-uta.wav", O_RDONLY);
    if ((fdo = open("/dev/dsp", O_WRONLY)) == -1) {
        perror("/dev/dsp");
        close(fdi);
        return 1;
    }
    arg = AFMT_S16_LE, ioctl(fdo, SNDCTL_DSP_SETFMT, &arg);
    arg = 2, ioctl(fdo, SNDCTL_DSP_CHANNELS, &arg);
    arg = 44100, ioctl(fdo, SNDCTL_DSP_SPEED, &arg);
    lseek(fdi, 44, SEEK_SET);
    while ((len = read(fdi, buf, 2048)) > 0)
        write(fdo, buf, len);
    close(fdi), close(fdo);
    return 0;
}

以下のようにコンパイルして実行することにより,音を出すことができます。このプログラムは後で使いますので,残しておいてください。

taro@host:~$ make dsptest
cc     dsptest.c   -o dsptest
taro@host:~$ ./dsptest

コンテナ内でサウンドを出力する(OSS)

それでは,いよいよコンテナ内で音を出すことを試してみましょう。あらかじめ,taroユーザがroot権限でdefaultバリアントのPlamoコンテナct01を作成し,そのコンテナ内で作成したhanakoユーザが音を出すことを試みます。

hanako@ct01:~$ cat pipipipi.au > /dev/audio
-bash: /dev/audio: 許可がありません
hanako@ct01:~$ ./dsptest
/dev/dsp: No such file or directory

コンテナ内に/dev/audio/dev/dspが存在しないため,音が出ませんね。そこで,ホストにいるtaroユーザが以下のコマンドを実行して,OSS互換オーディオデバイスファイルを作成します。

taro@host:~$ sudo lxc-attach -n ct01 -e -- \
> mknod -m 660 /dev/mixer c 14 0
taro@host:~$ sudo lxc-attach -n ct01 -e -- \
> mknod -m 660 /dev/sequencer c 14 1
taro@host:~$ sudo lxc-attach -n ct01 -e -- \
> mknod -m 660 /dev/dsp c 14 3
taro@host:~$ sudo lxc-attach -n ct01 -e -- \
> mknod -m 660 /dev/audio c 14 4
taro@host:~$ sudo lxc-attach -n ct01 -e -- \
> mknod -m 660 /dev/sequencer2 c 14 8
taro@host:~$ sudo lxc-attach -n ct01 -- \
> chmod +t /dev/{mixer,sequencer{,2},dsp,audio}
taro@host:~$ sudo lxc-attach -n ct01 -- \
> chgrp audio /dev/{mixer,sequencer{,2},dsp,audio}

また,オーディオデバイスにアクセスできるように,以下の設定をコンテナの設定ファイル/var/lib/lxc/ct01/configに追加して,コンテナを再起動します。

# Audio configuration
lxc.cgroup.devices.allow = c 14:* rwm

これらの設定により,音が出るかどうか再び試みます。

hanako@ct01:~$ cat pipipipi.au > /dev/audio
hanako@ct01:~$ ./dsptest

これで音が出るようになりました。

コンテナ内でサウンドを出力する(ALSA)

次に,ALSA APIを使用したaplayコマンドを使って音を出してみましょう。実行したところ,以下のエラーが出てしまいました。

hanako@ct01:~$ aplay pipipipi.au
ALSA lib confmisc.c:768:(parse_card) cannot find card '0'
ALSA lib conf.c:4246:(_snd_config_evaluate) function snd_func_card_driver returned error: \
そのようなファイルやディレクトリはありません
ALSA lib confmisc.c:392:(snd_func_concat) error evaluating strings
ALSA lib conf.c:4246:(_snd_config_evaluate) function snd_func_concat returned error: \
そのようなファイルやディレクトリはありません
ALSA lib confmisc.c:1251:(snd_func_refer) error evaluating name
ALSA lib conf.c:4246:(_snd_config_evaluate) function snd_func_refer returned error: \
そのようなファイルやディレクトリはありません
ALSA lib conf.c:4725:(snd_config_expand) Evaluate error: \
そのようなファイルやディレクトリはありません
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM default
aplay: main:696: audio open error: \
そのようなファイルやディレクトリはありません

はい,コンテナ内に/dev/snd/*が存在しないためですね。ホストにいるtaroユーザが以下のコマンドを実行して,/dev/sndディレクトリを作成します。

taro@host:~$ sudo lxc-attach -n ct01 -- mkdir -m 755 /dev/snd

また,/dev/sndをバインドマウントする処理を含めた以下の設定を,コンテナの設定ファイル/var/lib/lxc/ct01/configに追加して,コンテナを再起動します。

# Audio configuration
lxc.mount.entry = /dev/snd dev/snd none bind 0 0
lxc.cgroup.devices.allow = c 14:* rwm
lxc.cgroup.devices.allow = c 116:* rwm

これらの設定により,音が出るかどうか再び試みます。

hanako@ct01:~$ aplay pipipipi.au
再生中 Sparc オーディオ 'pipipipi.au' : Mu-Law, レート 8000 Hz, モノラル
hanako@ct01:~$ aplay geragerapo-no-uta.wav
再生中 WAVE 'geragerapo-no-uta.wav' : Signed 16 bit Little Endian, レート 44100 Hz, ステレオ

これで音が出るようになりました。

著者プロフィール

田向正一(たむきしょういち)

本業では,Linuxベースのクラウドシステムを開発する傍ら,いろいろなOSSコミュニティに首を突っ込んでいます。昨年あたりから,LXCの開発に参加するようになりました。vi ()をこよなく愛し,プログラミングからMIDIデータの打ち込みまで,viを使った生活環境にどっぷり浸っています。Plamo Linuxメンテナ。これからの世代の人たちに,UNIX/Linux文化の楽しさを広く伝えることを目標としています。

※ nviです。vimではありません。

コメント

コメントの記入