PulseAudio:シンプルAPI

PulseAudio
インクルードファイル
インクルードファイルは、/usr/include/pulse/ ディレクトリにあり、カテゴリごとにファイルが分かれています。

pulse/pulseaudio.h をインクルードすると、メインの各ファイルをインクルードできます。
ただし、simple.h と glib-mainloop.h に関しては、必要に応じて別途インクルードする必要があります。

#include <pulse/pulseaudio.h>
ライブラリ
ライブラリは複数あります。

libpulse.soメイン
libpulse-simple.soシンプル API
libpulse-mainloop-glib.soGLIB 2.x メインループのバインド

libpulse は必須で、他は必要に応じて追加します。
文字列
PulseAudio では、すべての文字列は UTF-8 として扱われます。
pulse/utf8.h に、文字列変換の関数があるので、ロケール文字列と UTF-8 文字列を変換したい場合は、そちらを使うこともできます。
シンプル API
PulseAudio を簡単に扱うために、ごくシンプルな API が用意されているので、まずはそちらを使ってみます。
複雑な操作はせず、単純に再生または録音だけしたい時に使えます。

pulse/simple.h のインクルードと、libpulse, libpulse-simple のリンクが必要になります。

シンプル API の関数一覧は、こちら
作成
まずは、pa_simple_new() を使って、PulseAudio サーバーに接続します。

pa_simple *pa_simple_new(const char *server, const char *name, pa_stream_direction_t dir,
    const char *dev, const char *stream_name, const pa_sample_spec *ss, const pa_channel_map *map,
    const pa_buffer_attr *attr, int *error);

server接続するサーバー名。NULL でデフォルト。
nameクライアントの名前 (アプリケーション名など)。
pavucontrol などの音量調節アプリで表示される。
dir再生と録音、どちらで開くか。
PA_STREAM_PLAYBACK : 再生
PA_STREAM_RECORD : 録音
dev接続するシンク名(出力)、または、ソース名(入力)。
NULL でデフォルト。
普通に再生/録音をするなら、通常は NULL を指定します。
stream_nameストリームを説明するための名前。
これも、pavucontrol などの音量調節アプリで表示される。
再生中の曲名や、クライアントの名前などを指定する。
ssサンプルスペック
mapチャンネルマップ。NULL でデフォルト。
attrバッファ情報。NULL でデフォルト。
errorNULL 以外の場合、関数が失敗したときに、エラーコードが格納される。
戻り値NULL で失敗

色々とパラメータを指定できますが、NULL でデフォルトを指定できるものは、基本的に NULL で構いません。
再生 or 録音と、名前の文字列、サンプルスペックだけは指定します。
サンプルスペック
pa_sample_spec 構造体で、再生/録音するサンプルの情報を指定します。
再生なら、書き込む時のデータ、録音なら、読み込む時のデータになります。

typedef struct pa_sample_spec {
    pa_sample_format_t format;
    uint32_t rate;
    uint8_t channels;
} pa_sample_spec;

formatサンプルのフォーマットタイプ。

PA_SAMPLE_U8
PA_SAMPLE_ALAW
PA_SAMPLE_ULAW
PA_SAMPLE_S16LE
PA_SAMPLE_S16BE
PA_SAMPLE_FLOAT32LE
PA_SAMPLE_FLOAT32BE
PA_SAMPLE_S32LE
PA_SAMPLE_S32BE
PA_SAMPLE_S24LE
PA_SAMPLE_S24BE
PA_SAMPLE_S24_32LE (32bit 幅の中の 24bit)
PA_SAMPLE_S24_32BE
rateサンプリングレート (Hz)
channelsチャンネル数

PA_SAMPLE_S16LE (16bit 符号付き, LE)、48000 (Hz)、2チャンネル、というように指定します。
解放
void pa_simple_free(pa_simple *s);

終了時は、pa_simple オブジェクトを解放します。
再生/録音
再生用にデータを書き込む時は、pa_simple_write()。
録音用にデータを取得する時は、pa_simple_read() を使います。

int pa_simple_write(pa_simple *s, const void *data, size_t bytes, int *error);
int pa_simple_read(pa_simple *s, void *data, size_t bytes, int *error);

戻り値は、成功時 0、エラー時は負の値です(シンプル API の他の関数も同様)。
error 引数で、エラー時に、エラーコードを取得できます(NULL 指定可)。

書き込み時は、data で指定されたバッファのデータは内部にコピーされるので、pa_simple_write() の実行後は、すぐにバッファを解放したり、別の値を書き込んでも問題ありません。

書き込みを行うと、自動的に再生が始まります。

ブロック
なお、これらの関数は、実行後すぐに戻ってくるとは限りません。
再生の場合、バッファのデータがある程度再生されてから、戻ってくる場合があります。

関数内部では、再生バッファ内で、常に一定程度のサイズまでの未再生データを保持するようにしているため、書き込み済みで未再生のデータが多い場合(一度の書き込みでサイズが多い場合)は、再生を行いつつ、バッファに新しいデータを書き込めるようになるまで、ブロックして待ちます。

そのようにして再生しつつ、すべてのデータがサーバーに送信できたら、関数は戻ってきます。
再生が終わるまで待つ
int pa_simple_drain(pa_simple *s, int *error);

再生時、書き込み済みのデータが、実際にすべて再生し終わるまで待ちます(ブロックする)。
オーディオデータを破棄
int pa_simple_flush(pa_simple *s, int *error);

再生/録音バッファをフラッシュします。
再生用にすでに書き込まれたデータ、または、録音用で読み込み待ちになっているデータを破棄します。
ソースコード
シンプル API を使って、正弦波の 'ドレミ' を1秒ずつ、合計3秒再生するプログラムです。
16bit LE、48000 Hz、モノラルで出力します。

$ cc -o 01-simple 01-simple.c -lpulse -lpulse-simple -lm

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <pulse/simple.h>

#define SAMPRATE 48000
#define PI 3.141592653

//正弦波 1秒 書き込み

static void _write_audio(pa_simple *ps,uint8_t *buf,double freq)
{
    int i;
    int16_t n;
    double d;
    uint8_t *ptr = (uint8_t *)buf;

    for(i = 0; i < SAMPRATE; i++, ptr += 2)
    {
        d = sin(2 * PI * freq * ((double)i / SAMPRATE)) * 0.8;
        n = (int16_t)round(d * 32767);

        //16bit LE
        ptr[0] = (uint8_t)n;
        ptr[1] = n >> 8;
    }

    printf("write..");
    fflush(stdout);

    pa_simple_write(ps, buf, SAMPRATE * 2, NULL);

    printf("done\n");
}

int main(void)
{
    pa_simple *ps;
    pa_sample_spec ss;
    int err;
    uint8_t *buf;

    //作成

    ss.format = PA_SAMPLE_S16LE;
    ss.rate = SAMPRATE;
    ss.channels = 1;

    ps = pa_simple_new(NULL, "pulse-test", PA_STREAM_PLAYBACK,
        NULL, "pulse-test-stream", &ss, NULL, NULL, &err);

    if(!ps)
    {
        printf("error: %d\n", err);
        return 1;
    }

    //書き込み

    buf = (uint8_t *)malloc(SAMPRATE * 2);

    _write_audio(ps, buf, 261.626); //ド
    _write_audio(ps, buf, 293.665); //レ
    _write_audio(ps, buf, 329.628); //ミ

    free(buf);

    //再生が終わるまで待つ

    pa_simple_drain(ps, NULL);

    //解放

    pa_simple_free(ps);

    return 0;
}
解説
pa_simple の作成時、name は "pulse-test"、stream_name は "pulse-test-stream" に指定しています。

プログラムの実行中に pavucontrol の「再生」タブを見てみると、以下のように表示されます。



このように、PulseAudio で再生/録音中のストリームの情報を、UI で表示するために使用されます。
再生
pa_simple_write() の実行前に "write.." が表示され、関数が戻ると "done" が表示されます。

実際に実行してみると、pa_simple_write() が実行された後は、しばらく再生されてから、関数が戻ってくるのがわかります。

最後の pa_simple_write() で書き込んだデータは、まだ再生されていない部分が残っている可能性があるので、pa_simple_drain() で、すべてのデータが再生されるまで待ちます。