ALSA:PCM ステータス

PCM ステータス
PCM の現在のステータスを取得するには、snd_pcm_status() を実行します。

ステータス関連の関数は、ステータス をご覧ください。
PCM ステータスの取得
int snd_pcm_status(snd_pcm_t *pcm, snd_pcm_status_t *status);

PCM の現在のステータス情報を取得します。

snd_pcm_status_t は、あらかじめ確保しておく必要があります。
snd_pcm_status_t
//サイズ取得
size_t snd_pcm_status_sizeof(void);

//確保
int snd_pcm_status_malloc(snd_pcm_status_t **ptr);

//解放
void snd_pcm_status_free(snd_pcm_status_t *obj);
PCM 状態の取得
PCM 状態 (state) は、PCM が実行中かなどの状態を示します。

//snd_pcm_status_t から取得
snd_pcm_state_t snd_pcm_status_get_state(const snd_pcm_status_t *obj);

//PCM から直接取得
snd_pcm_state_t snd_pcm_state(snd_pcm_t *pcm);
PCM 状態
snd_pcm_state_t は、以下のような値です。

SND_PCM_STATE_OPEN開いている。
snd_pcm_open() 後。
SND_PCM_STATE_SETUP構成がインストールされている。
snd_pcm_prepare() を待機している状態。
再生/録音が停止されると、この状態になる。
SND_PCM_STATE_PREPAREDsnd_pcm_prepare() が行われ、PCM を開始する準備ができている。
SND_PCM_STATE_RUNNING再生/録音が実行されている。
snd_pcm_drop() または snd_pcm_drain() で停止できる。
SND_PCM_STATE_XRUNアンダーラン (再生) またはオーバーラン (録音) 状態。
読み書き時に -EPIPE が返った場合は、この状態である。
回復するには、snd_pcm_recover() か prepare を実行する。
SND_PCM_STATE_DRAININGdrain によって停止を待っている
SND_PCM_STATE_PAUSEDsnd_pcm_pause() による一時停止中
SND_PCM_STATE_SUSPENDEDハードウェアがサスペンド状態。
snd_pcm_resume() で再開できる。
SND_PCM_STATE_DISCONNECTEDハードウェアが切断されている
状態の遷移
まず、snd_pcm_open() 後、SND_PCM_STATE_OPEN 状態になります。

snd_pcm_hw_params() でハードウェア構成をインストールした場合、まずは SND_PCM_STATE_SETUP 状態になります。
関数内で自動的に snd_pcm_prepare() が実行されるので、インストールされた構成で PCM の準備が行われた後、SND_PCM_STATE_PREPARED 状態になります。

snd_pcm_hw_params() が戻った後は、PREPARED 状態になるので、SETUP 状態は捕捉できません。

PREPARED 状態は、再生や録音を開始する準備が出来ているということなので、再生/録音を開始するなら、snd_pcm_start() を実行します。
これにより、SND_PCM_STATE_RUNNING 状態になります。

snd_pcm_drop() で PCM を停止させると、SND_PCM_STATE_SETUP 状態になります。
この後、再び PCM を実行させたいなら、snd_pcm_prepare() を行う必要があります。
プログラム
実際に、PCM 状態の変化を見てみます。

$ cc -o 13-state 13-state.c -lasound

#include <stdio.h>
#include <alsa/asoundlib.h>

static void _put_state(snd_pcm_t *pcm)
{
    snd_pcm_state_t state;

    state = snd_pcm_state(pcm);

    printf("> %s\n", snd_pcm_state_name(state));
}

int main(void)
{
    snd_pcm_t *pcm;
    snd_pcm_uframes_t buf,period;

    if(snd_pcm_open(&pcm, "default", SND_PCM_STREAM_PLAYBACK, 0))
    {
        printf("open error\n");
        return 1;
    }

    _put_state(pcm);

    //ハードウェア構成

    snd_pcm_set_params(pcm, SND_PCM_FORMAT_S16_LE,
        SND_PCM_ACCESS_MMAP_INTERLEAVED, 1, 48000, 1, 100 * 1000);

    _put_state(pcm);

    snd_pcm_get_params(pcm, &buf, &period);

    printf("buf size: %lu\nperiod size: %lu\n", buf, period);

    //開始

    snd_pcm_start(pcm);
    
    _put_state(pcm);

    //

    snd_pcm_close(pcm);

    return 0;
}
実行結果
> OPEN
> PREPARED
buf size: 5120
period size: 1024
> RUNNING

「PCM を開いた後」「ハードウェア構成のインストール後」「再生の開始後」に、それぞれ PCM 状態を出力しています。

なお、ここではデータを全く書き込んでいませんが、特に問題は起きません。
ハードウェア構成を簡単にセット
ハードウェア構成の設定は少々面倒なので、snd_pcm_set_params() を使って、一部の値のみを指定して、簡単にインストールしています。

int snd_pcm_set_params(snd_pcm_t *pcm, snd_pcm_format_t format, snd_pcm_access_t access,
    unsigned int channels, unsigned int rate, int soft_resample, unsigned int latency);

soft_resampleリサンプリングを許可するか
latencyマイクロ秒単位。
バッファの長さの目安になる。
バッファとピリオドのサイズを取得
int snd_pcm_get_params(snd_pcm_t *pcm, snd_pcm_uframes_t *buffer_size, snd_pcm_uframes_t *period_size);

snd_pcm_hw_params_t を使わずに、バッファとピリオドのサイズ (フレーム単位) を取得します。
バッファの長さ
ここでは、latency を 100 ms に指定しています。

バッファの長さを 100 ms にしたい場合、フレーム数は以下のように計算できます。

frames = time(us) / (1000 * 1000) * samprate(Hz)

(100*1000)/(1000*1000)*48000 = 4800 frames です。

ただし、バッファの長さはピリオド単位になるので、1024 フレーム単位で 4800 フレームが収まる値は、1024 * 5 = 5120 になります。
結果、バッファの長さは 5120 フレームになっています。

なお、バッファの長さの最大値は、PCM ごとに決まっているので、latency に大きい値を指定しても、一定以上の値にはなりません。