ストリーム
ここから、再生/録音のメインとなる、ストリームを扱っていきます。
ストリームを作成する関数は、3つあります。
PCM で出力するなら、通常は pa_stream_new() を使います。
さらに、プロパティを指定したい場合は、pa_stream_new_with_proplist() を使います。
PCM または圧縮エンコードで出力したい場合は、pa_stream_new_extended() を使います。
pa_stream は、クライアントが、自身で作成したストリームを識別するためのオブジェクトです。
ストリームの解放関数はないので、使用後は参照カウンタを -1 します。
作成したストリームは、再生用ならサーバー上のシンクに、録音用なら、サーバー上のソースと接続する必要があります。
接続されると、サーバー上で、シンク入力またはソース出力が作成されます。
flags で、ミュート関連のフラグ指定がない場合、ストリームをミュート状態にするかどうかは、サーバーが選択します。
volume 引数での音量指定では、ver 0.9.20 以降の場合、シンクがフラットボリュームモード (struct pa_sink_info の flags で、PA_SINK_FLAT_VOLUME が ON) の場合は絶対音量で、それ以外は相対音量になります。
基本的には相対音量になるので、全体的な絶対音量はシンクで指定し、必要であれば、再生ストリームごとに相対音量を指定することになります。
ストリームを、サーバー上のシンク/ソースから切断します。
これにより、再生出力などは行われなくなります。
不要になったストリームは、切断した後、pa_stream_unref() で解放します。
ストリームの作成
pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map); //プロパティリストを指定 pa_stream *pa_stream_new_with_proplist(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, pa_proplist *p); //圧縮エンコード対応 pa_stream *pa_stream_new_extended(pa_context *c, const char *name, pa_format_info *const *formats, unsigned int n_formats, pa_proplist *p);
ストリームを作成する関数は、3つあります。
PCM で出力するなら、通常は pa_stream_new() を使います。
さらに、プロパティを指定したい場合は、pa_stream_new_with_proplist() を使います。
PCM または圧縮エンコードで出力したい場合は、pa_stream_new_extended() を使います。
pa_stream は、クライアントが、自身で作成したストリームを識別するためのオブジェクトです。
参照カウント
pa_stream *pa_stream_ref(pa_stream *s); void pa_stream_unref(pa_stream *s);
ストリームの解放関数はないので、使用後は参照カウンタを -1 します。
サーバーと接続
//再生用にシンクと接続 int pa_stream_connect_playback(pa_stream *s, const char *dev, const pa_buffer_attr *attr, pa_stream_flags_t flags, const pa_cvolume *volume, pa_stream *sync_stream); //録音用にソースと接続 int pa_stream_connect_record(pa_stream *s, const char *dev, const pa_buffer_attr *attr, pa_stream_flags_t flags);
作成したストリームは、再生用ならサーバー上のシンクに、録音用なら、サーバー上のソースと接続する必要があります。
接続されると、サーバー上で、シンク入力またはソース出力が作成されます。
dev | 接続するシンクまたはソースの名前。 NULL でサーバーが選択する(通常は NULL で良い)。 |
---|---|
attr | 再生バッファの属性。NULL でデフォルト。 |
flags | フラグ。 (以下、一部抜粋) PA_STREAM_START_CORKED : ストリームを停止状態で作成。 PA_STREAM_START_MUTED : ストリームをミュート状態で作成。 PA_STREAM_START_UNMUTED : ストリームをミュート解除状態で作成。 PA_STREAM_RELATIVE_VOLUME : volume 引数の値を、常に相対音量として扱う。 |
volume | (再生用) ストリームの初期音量。NULL でデフォルト。 相対的または絶対的な音量として扱われるかは、シンクの状態やフラグによる。 ver 5.0 以降の場合、1つのチャンネルの音量しか指定されてない場合、すべてのチャンネルにその音量が適用される。 |
sync_stream | NULL 以外の場合、指定ストリームと同期される |
戻り値 | 成功時は 0 |
flags で、ミュート関連のフラグ指定がない場合、ストリームをミュート状態にするかどうかは、サーバーが選択します。
volume 引数での音量指定では、ver 0.9.20 以降の場合、シンクがフラットボリュームモード (struct pa_sink_info の flags で、PA_SINK_FLAT_VOLUME が ON) の場合は絶対音量で、それ以外は相対音量になります。
基本的には相対音量になるので、全体的な絶対音量はシンクで指定し、必要であれば、再生ストリームごとに相対音量を指定することになります。
切断
int pa_stream_disconnect(pa_stream *s);
ストリームを、サーバー上のシンク/ソースから切断します。
これにより、再生出力などは行われなくなります。
不要になったストリームは、切断した後、pa_stream_unref() で解放します。
準備ができるのを待つ
サーバーとの接続時と同様に、ストリームを作成または接続しても、すぐに操作が完了するとは限りません。
ストリームの現在の状態を取得した上で、準備ができるのを待つ必要があります。
ストリームの現在の状態を取得します。
ストリームの状態が変化した時に呼び出される、コールバック関数をセットします。
ストリームの現在の状態を取得した上で、準備ができるのを待つ必要があります。
状態の取得
pa_stream_state_t pa_stream_get_state(const pa_stream *p);
ストリームの現在の状態を取得します。
PA_STREAM_UNCONNECTED | ストリームはまだ、シンクまたはソースに接続されていない |
---|---|
PA_STREAM_CREATING | ストリームを作成中 |
PA_STREAM_READY | ストリームが準備できた。オーディオデータを渡すことができる。 |
PA_STREAM_FAILED | ストリームが無効になるエラーが発生した |
PA_STREAM_TERMINATED | ストリームは正常に終了した |
ストリームの状態変化時のコールバックをセット
void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata); //コールバック関数 typedef void (*pa_stream_notify_cb_t)(pa_stream *p, void *userdata);
ストリームの状態が変化した時に呼び出される、コールバック関数をセットします。
プログラム
ストリームを作成した後、削除するプログラムです。
また、サーバーイベントの通知を設定して、シンク入力のイベントを出力しています。
また、サーバーイベントの通知を設定して、シンク入力のイベントを出力しています。
$ cc -o 11-stream 11-stream.c util.c -lpulse
#include <stdio.h> #include <pulse/pulseaudio.h> #include "util.h" PulseData *pulse; //ストリームの状態変化 static void _cb_state(pa_stream *strm,void *data) { int state = pa_stream_get_state(strm); switch(state) { case PA_STREAM_UNCONNECTED: printf("UNCONNECTED\n"); break; case PA_STREAM_CREATING: printf("CREATING\n"); break; case PA_STREAM_READY: printf("READY\n"); break; case PA_STREAM_FAILED: printf("FAILED\n"); break; case PA_STREAM_TERMINATED: printf("TERMINATED\n"); break; } if(state == PA_STREAM_READY || state == PA_STREAM_FAILED || state == PA_STREAM_TERMINATED) { pa_threaded_mainloop_signal(pulse->mainloop, 0); } } //ストリームの作成と接続 static pa_stream *_create_stream(PulseData *p) { pa_stream *strm; pa_sample_spec ss; pa_stream_state_t state; ss.format = PA_SAMPLE_S16LE; ss.rate = 48000; ss.channels = 1; strm = pa_stream_new(p->ctx, "test-stream", &ss, NULL); pa_stream_set_state_callback(strm, _cb_state, NULL); if(pa_stream_connect_playback(strm, NULL, NULL, 0, NULL, NULL)) { printf("! failed connect\n"); pa_stream_unref(strm); return NULL; } while(1) { state = pa_stream_get_state(strm); if(state == PA_STREAM_READY || state == PA_STREAM_FAILED) break; pa_threaded_mainloop_wait(p->mainloop); } return strm; } //サーバーイベント static void _event_callback(pa_context *c,pa_subscription_event_type_t t,uint32_t idx,void *userdata) { int n = t & PA_SUBSCRIPTION_EVENT_TYPE_MASK; const char *mes = NULL; switch(n) { case PA_SUBSCRIPTION_EVENT_NEW: mes = "NEW"; break; case PA_SUBSCRIPTION_EVENT_CHANGE: mes = "CHANGE"; break; case PA_SUBSCRIPTION_EVENT_REMOVE: mes = "REMOVE"; break; } printf("sink-input: %s, idx=%d\n", mes, idx); } static pa_operation *_set_event(PulseData *p,void *data) { pa_context_set_subscribe_callback(p->ctx, _event_callback, NULL); return pa_context_subscribe(p->ctx, PA_SUBSCRIPTION_MASK_SINK_INPUT, pulse_success_callback_signal, p); } int main(void) { pa_stream *strm; pulse = pulse_connect(0); if(!pulse) return 1; //サーバーイベントをセット pulse_wait_operation(pulse, _set_event, NULL); //ストリームの作成 pa_threaded_mainloop_lock(pulse->mainloop); strm = _create_stream(pulse); if(strm) { pa_stream_disconnect(strm); //実際に終了するまで待つ (REMOVE イベント取得のため) while(pa_stream_get_state(strm) != PA_STREAM_TERMINATED) pa_threaded_mainloop_wait(pulse->mainloop); pa_stream_unref(strm); } pa_threaded_mainloop_unlock(pulse->mainloop); pulse_free(pulse); return 0; }
解説
実行すると、以下のように出力されます。
ストリームの作成後、コールバック関数を設定して、接続を開始し、準備ができるか失敗するまで待ちます。
実際に準備が出来た段階で、サーバー上に、新しいシンク入力が作成されています。
その後はすぐに切断し、ストリームの状態が TERMINATED になって、正常に終了するまで待ちます。
これは、シンク入力の REMOVE イベントを出力するためです。
待たずにプログラムを終了してしまうと、その後のサーバーイベントを受け取る機会がないので、REMOVE が出力されません。
通常は待つ必要はないので、切断後すぐに unref して構いません。
CREATING READY sink-input: NEW, idx=11 TERMINATED sink-input: REMOVE, idx=11
ストリームの作成後、コールバック関数を設定して、接続を開始し、準備ができるか失敗するまで待ちます。
実際に準備が出来た段階で、サーバー上に、新しいシンク入力が作成されています。
その後はすぐに切断し、ストリームの状態が TERMINATED になって、正常に終了するまで待ちます。
これは、シンク入力の REMOVE イベントを出力するためです。
待たずにプログラムを終了してしまうと、その後のサーバーイベントを受け取る機会がないので、REMOVE が出力されません。
通常は待つ必要はないので、切断後すぐに unref して構いません。
シンク入力/ソース出力のインデックスを取得
pa_stream_get_index() を使うと、ストリームのシンク入力/ソース出力のインデックスを取得することができます。
uint32_t pa_stream_get_index(const pa_stream *s);