録音
オーディオ入力からの録音を行う場合は、ストリームを作成した後、pa_stream_connect_record() を実行して、ストリームを、サーバー上の1つのソースに接続します。
これにより、サーバー上で、それに対応するソース出力が作成されます。
その他の操作は、再生時と同じような形になります。
録音用に、ストリームを、サーバー上のソースと接続します。
dev は、接続するソースの名前です。NULL でサーバーが選択します。
これにより、サーバー上で、それに対応するソース出力が作成されます。
その他の操作は、再生時と同じような形になります。
録音用に接続
int pa_stream_connect_record(pa_stream *s, const char *dev, const pa_buffer_attr *attr, pa_stream_flags_t flags);
録音用に、ストリームを、サーバー上のソースと接続します。
dev は、接続するソースの名前です。NULL でサーバーが選択します。
フラグメントサイズ
録音ストリームの場合、pa_buffer_attr 構造体のバッファ情報は、maxlength と fragsize のみ有効となります。
サーバーは、fragsize バイトのブロック単位で、データを送信します。
サーバーは、fragsize バイトのブロック単位で、データを送信します。
録音バッファ
サーバーは、録音用にバッファを作成します。
オーディオデータが入力されると、サーバーは録音バッファにデータを書き込んでいきます。
タイミング情報で言うところの write_index は、サーバーが次に書き込むバッファ位置になります。
read_index は、クライアントが次に読み込む位置です。
再生バッファとは逆に、PulseAudio が自動でデータを書き込んでいくので、クライアントは、バッファが溢れないように、順次データを読み込んでいく必要があります。
録音の場合は、クライアントの読み込みが追いつかず、バッファが溢れた場合、バッファオーバーフローになります。
オーディオデータが入力されると、サーバーは録音バッファにデータを書き込んでいきます。
タイミング情報で言うところの write_index は、サーバーが次に書き込むバッファ位置になります。
read_index は、クライアントが次に読み込む位置です。
再生バッファとは逆に、PulseAudio が自動でデータを書き込んでいくので、クライアントは、バッファが溢れないように、順次データを読み込んでいく必要があります。
録音の場合は、クライアントの読み込みが追いつかず、バッファが溢れた場合、バッファオーバーフローになります。
データを読み込む
読み込み
int pa_stream_peek(pa_stream *p, const void **data, size_t *nbytes);
録音バッファに記録されたオーディオデータを読み込みます。
ただし、この関数は、録音バッファの読み込み位置を移動することはしません。
この関数で取得したポインタを使ってデータを処理した後、pa_stream_drop() を実行することで、読み込んだデータを削除し、録音バッファの読み込み位置を移動します。
data にデータのポインタが、nbytes にデータのバイト数が返ります。
バッファが空の場合は、*data = NULL, *nbytes = 0 になります。
この場合、drop は行わないようにしてください。
記録したデータに穴がある場合(途中で入力が途切れた場合)、*data = NULL, *nbytes = 穴のサイズとなります。
成功時は 0、失敗時は負の値が返ります。
読み込みデータの削除
int pa_stream_drop(pa_stream *p);
前回実行した pa_stream_peek() で取得した、読み込みデータを削除します。
バッファが空だった場合は呼び出す必要はありませんが、穴のデータだった場合は、呼び出す必要があります。
読み込み可能なサイズ
size_t pa_stream_readable_size(const pa_stream *p);
pa_stream_peek() で読み込むことができるバイト数を返します。
エラー時は、(size_t)-1 が返ります。
peek 時、実際にこのサイズ分を、一気に読み込めるとは限りません。
コールバック
void pa_stream_set_read_callback(pa_stream *p, pa_stream_request_cb_t cb, void *userdata); //コールバック関数 typedef void (*pa_stream_request_cb_t)(pa_stream *p, size_t nbytes, void *userdata);
新しいデータが読み込み可能になった時に実行される、コールバック関数をセットします。
プログラム
16bit, 48000 Hz, 2ch で、約5秒間録音を行い、record.wav に出力するプログラムです。
$ cc -o 17-record 17-record.c util.c -lpulse
#include <stdio.h> #include <stdlib.h> #include <pulse/pulseaudio.h> #include "util.h" #define SAMPRATE 48000 PulseData *pulse; //読み込み可能になった時 static void _cb_read(pa_stream *p,size_t nbytes,void *userdata) { printf("- readable: %d\n", (int)nbytes); pa_threaded_mainloop_signal(pulse->mainloop, 0); } //読み込み (5秒分) static uint32_t _read_data(PulseData *p,FILE *fp) { pa_stream *strm = p->stream; const void *readbuf; size_t readsize; uint32_t writesize = 0; pa_threaded_mainloop_lock(p->mainloop); while(writesize < SAMPRATE * 4 * 5) { //読み込み可能なサイズ取得 while(1) { readsize = pa_stream_readable_size(strm); if(readsize == (size_t)-1) goto END; if(readsize) break; pa_threaded_mainloop_wait(p->mainloop); } //読み込み if(pa_stream_peek(strm, &readbuf, &readsize)) break; printf("* peek: %c %d\n", (readbuf)? 'o':'-', (int)readsize); //書き込み if(readbuf) { fwrite(readbuf, 1, readsize, fp); writesize += readsize; } //削除 if(readsize) pa_stream_drop(strm); } END: pa_threaded_mainloop_unlock(p->mainloop); return writesize; } //ストリームの状態変化コールバック static void _cb_stream_state(pa_stream *strm,void *data) { int state = pa_stream_get_state(strm); if(state == PA_STREAM_READY || state == PA_STREAM_FAILED) pa_threaded_mainloop_signal(pulse->mainloop, 0); } //録音用に接続 static int _connect_record(PulseData *p) { pa_stream *strm; pa_sample_spec ss; pa_stream_state_t state; pa_threaded_mainloop_lock(p->mainloop); //作成 ss.format = PA_SAMPLE_S16LE; ss.rate = SAMPRATE; ss.channels = 2; strm = pa_stream_new(p->ctx, "test-stream", &ss, NULL); //待つ pa_stream_set_state_callback(strm, _cb_stream_state, p); if(pa_stream_connect_record(strm, NULL, NULL, 0)) { printf("! failed stream connect\n"); pa_stream_unref(strm); pa_threaded_mainloop_unlock(p->mainloop); return 1; } while(1) { state = pa_stream_get_state(strm); if(state == PA_STREAM_READY || state == PA_STREAM_FAILED) break; pa_threaded_mainloop_wait(p->mainloop); } // p->stream = strm; printf("# stream connected\n"); pa_threaded_mainloop_unlock(p->mainloop); return 0; } int main(void) { FILE *fp; uint32_t size; pulse = pulse_connect(0); if(!pulse) return 1; if(_connect_record(pulse)) pulse_enderr(pulse); pulse_put_buffer_attr(pulse->stream); //コールバック pa_threaded_mainloop_lock(pulse->mainloop); pa_stream_set_read_callback(pulse->stream, _cb_read, NULL); pa_threaded_mainloop_unlock(pulse->mainloop); //録音 fp = fopen("record.wav", "wb"); if(!fp) goto END; pulse_write_wave_header(fp, SAMPRATE, 2); size = _read_data(pulse, fp); printf("* write size: %u\n", size); pulse_write_wave_result_size(fp, size); fclose(fp); // END: pulse_free(pulse); return 0; }
実行結果
# stream connected --- buffer --- maxlength: 4194304 tlength: 48000 prebuf: 4294967295 minreq: 4294967295 fragsize: 384000 -------------- - readable: 65472 - readable: 130944 - readable: 196416 - readable: 261888 - readable: 327360 - readable: 349280 * peek: o 65472 * peek: o 65472 * peek: o 65472 * peek: o 65472 * peek: o 65472 * peek: o 21920 - readable: 3520 - readable: 68992 * peek: o 3520 * peek: o 65472 - readable: 65472 - readable: 130944 - readable: 196416 - readable: 261888 - readable: 280352 * peek: o 65472 * peek: o 65472 * peek: o 65472 * peek: o 65472 * peek: o 18464 - readable: 6976 * peek: o 6976 - readable: 65472 - readable: 130944 - readable: 196416 - readable: 261888 - readable: 327360 - readable: 342304 * peek: o 65472 * peek: o 65472 * peek: o 65472 * peek: o 65472 * write size: 967488
pa_stream_readable_size() で 1 以上が返ったら、データを読み込み、ファイルに出力します。
fragsize の値を見てみると、384000 byte (2秒分) です。
最初に、読み込み可能になったというコールバックが何回か続きますが、この間、読み込みは行われていません。
つまり、読み込みは可能であるが、pa_stream_readable_size() で 0 が返っているので、読み込み可能なサイズがどんどん増えている状態です。
その後、読み込み可能なサイズが 349280 になった時、実際に読み込みが行われています。
これは fragsize に近い値なので、pa_stream_readable_size() は、ある程度読み込み可能な状態であっても、fragsize のサイズを基準にして、値を返しているようです。
pa_stream_readable_size() で 1 以上が返ったら、実際に読み込みを行います。
最初に peek で取得されたサイズは 65472 で、その後も、これ以上のサイズでは取得されていないので、読み込み用のバッファの最大値は、65472 byte であると思われます。
実際に読み込めるサイズと、peek で取得できるバッファの最大サイズは異なることがわかります。
書き込みサイズが5秒分を超えると、プログラムは終了します。
ちなみに、オーディオ入力が全くない(端子が1つも接続されていない)状態でも、録音はできます。
その場合、データは無音になります。