ソフトウェア構成
PCM には、再生/録音の実行中でも変更できる、ソフトウェア構成があります。
ハードウェア構成と同じように、snd_pcm_sw_params_t を確保した後、関数で値を設定し、構成をインストールします。
関数の詳細は、ソフトウェア構成 をご覧ください。
ソフトウェア構成をインストールします。
現在のソフトウェア構成を取得します。
バッファに読み書き可能なフレーム数が、このしきい値以上になった場合、PCM は SND_PCM_STATE_XRUN 状態となり、自動的に停止します。
この値が、snd_pcm_sw_params_get_boundary() で取得した境界位置と同じ場合は、自動停止せず、リングバッファ内で無限ループが行われます。
ハードウェア構成と同じように、snd_pcm_sw_params_t を確保した後、関数で値を設定し、構成をインストールします。
関数の詳細は、ソフトウェア構成 をご覧ください。
snd_pcm_sw_params_t
//サイズ取得 size_t snd_pcm_sw_params_sizeof(void); //確保 int snd_pcm_sw_params_malloc(snd_pcm_sw_params_t **ptr); //解放 void snd_pcm_sw_params_free(snd_pcm_sw_params_t *obj);
構成のインストール
int snd_pcm_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params);
ソフトウェア構成をインストールします。
現在の構成の取得
int snd_pcm_sw_params_current(snd_pcm_t *pcm, snd_pcm_sw_params_t *params);
現在のソフトウェア構成を取得します。
自動停止のしきい値をセット
int snd_pcm_sw_params_set_stop_threshold(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val);
バッファに読み書き可能なフレーム数が、このしきい値以上になった場合、PCM は SND_PCM_STATE_XRUN 状態となり、自動的に停止します。
この値が、snd_pcm_sw_params_get_boundary() で取得した境界位置と同じ場合は、自動停止せず、リングバッファ内で無限ループが行われます。
プログラム
ソフトウェア構成の現在の値を取得し、自動停止のしきい値を境界と同じ値にして、自動停止を無効にします。
バッファの長さ分のデータのみ書き込み、Enter キーが押されるまで待ちます。
その後、現在のステータスを出力して終了します。
バッファの長さ分のデータのみ書き込み、Enter キーが押されるまで待ちます。
その後、現在のステータスを出力して終了します。
$ cc -o 17-swparam 17-swparam.c util.c -lasound
#include <stdio.h> #include <stdlib.h> #include <alsa/asoundlib.h> #include "util.h" #define SAMPRATE 48000 static void _sw_params(snd_pcm_t *pcm) { snd_pcm_sw_params_t *sw; snd_pcm_uframes_t uframes,boundary; snd_pcm_tstamp_t tsmode; snd_pcm_tstamp_type_t tstype; int nval; snd_pcm_sw_params_malloc(&sw); //現在の値を取得 snd_pcm_sw_params_current(pcm, sw); snd_pcm_sw_params_get_boundary(sw, &boundary); printf("boundary: %lu frames (0x%lX)\n", boundary, boundary); snd_pcm_sw_params_get_tstamp_mode(sw, &tsmode); printf("tstamp mode: %s\n", snd_pcm_tstamp_mode_name(tsmode)); snd_pcm_sw_params_get_tstamp_type(sw, &tstype); printf("tstype: %d\n", tstype); snd_pcm_sw_params_get_avail_min(sw, &uframes); printf("avail min: %lu frames\n", uframes); snd_pcm_sw_params_get_period_event(sw, &nval); printf("period event: %d\n", nval); snd_pcm_sw_params_get_start_threshold(sw, &uframes); printf("start threshold: %lu\n", uframes); snd_pcm_sw_params_get_stop_threshold(sw, &uframes); printf("stop threshold: %lu\n", uframes); snd_pcm_sw_params_get_silence_threshold(sw, &uframes); printf("silence threshold: %lu\n", uframes); snd_pcm_sw_params_get_silence_size(sw, &uframes); printf("silence size: %lu\n", uframes); //セット snd_pcm_sw_params_set_tstamp_mode(pcm, sw, SND_PCM_TSTAMP_ENABLE); snd_pcm_sw_params_set_stop_threshold(pcm, sw, boundary); snd_pcm_sw_params(pcm, sw); // snd_pcm_sw_params_free(sw); } static void _put_ts(const char *name,snd_htimestamp_t *t) { printf("%s: %ld sec %ld ns\n", name, t->tv_sec, t->tv_nsec); } static void _get_status(snd_pcm_t *pcm) { snd_pcm_status_t *st; snd_htimestamp_t hts; snd_pcm_audio_tstamp_report_t rep; snd_pcm_status_malloc(&st); snd_pcm_status(pcm, st); printf("-- status --\n"); printf("state: %s\n", snd_pcm_state_name(snd_pcm_status_get_state(st))); snd_pcm_status_get_trigger_htstamp(st, &hts); _put_ts("trigger", &hts); snd_pcm_status_get_htstamp(st, &hts); _put_ts("tstamp", &hts); snd_pcm_status_get_audio_htstamp(st, &hts); _put_ts("audio", &hts); snd_pcm_status_get_audio_htstamp_report(st, &rep); printf("audio report: type(%d) report(%d) accuracy(%d)\n", rep.actual_type, rep.accuracy_report, rep.accuracy); snd_pcm_status_get_driver_htstamp(st, &hts); _put_ts("driver", &hts); printf("delay: %ld\n", snd_pcm_status_get_delay(st)); printf("avail: %lu\n", snd_pcm_status_get_avail(st)); printf("overrange: %lu\n", snd_pcm_status_get_overrange(st)); snd_pcm_status_free(st); } int main(int argc,char **argv) { snd_pcm_t *pcm; snd_pcm_uframes_t bufs,periods; uint8_t *buf; if(snd_pcm_open(&pcm, (argc >= 2)? argv[1]: "default", SND_PCM_STREAM_PLAYBACK, 0)) { return 1; } //ハードウェア構成 (16bit LE, 2ch) snd_pcm_set_params(pcm, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED, 2, SAMPRATE, 1, 500 * 1000); snd_pcm_get_params(pcm, &bufs, &periods); //ソフトウェア構成 _sw_params(pcm); //バッファの長さ分書き込み buf = (uint8_t *)malloc(bufs * 4); util_write_buf_sawtooth(buf, bufs, SAMPRATE, AUDIO_FREQ_DO); snd_pcm_writei(pcm, buf, bufs); free(buf); //Enter が押されるまで待つ getchar(); _get_status(pcm); // snd_pcm_drop(pcm); snd_pcm_close(pcm); return 0; }
実行結果
boundary: 4611686018427387904 frames (0x4000000000000000) tstamp mode: NONE tstype: 1 avail min: 1024 frames period event: 0 start threshold: 16384 stop threshold: 16384 silence threshold: 0 silence size: 0 -- status -- state: RUNNING trigger: 14144 sec 873074695 ns tstamp: 14146 sec 434646671 ns audio: 1 sec 565817458 ns audio report: type(0) report(0) accuracy(0) driver: 14146 sec 434650490 ns delay: -58608 avail: 74992 overrange: 0
ソフトウェア構成
- boundary は、リングバッファの境界位置、つまり、リングバッファの長さとなります。
見る限り、とても大きな値になっています。 - avail min は、poll() などで読み書きが可能になるまで待つ時の、しきい値となります。
このフレーム数以上が読み書き可能であれば、イベントが起きます。 - start threshold は、自動開始のしきい値です。
PCM が開始されていない状態で、リングバッファ内に指定値以上のフレーム数のデータが存在すれば、自動的に PCM を開始します。
ここでは、バッファの長さと同じになっています。 - stop threshold は、自動停止のしきい値です。
ここも、バッファの長さと同じになっています。
つまり、バッファがすべて空の状態になったら、自動停止します。
動作
自動停止のしきい値を、boundary と同じ値にセットして、自動停止を無効にします。
その後、バッファと同じ長さのデータを書き込み、Enter キーが押されるまで待ちます。
再生自体は、バッファの長さ分が終わると、無音になります。
無音状態になった後に Enter キーを押し、現在のステータスを出力します。
その後、バッファと同じ長さのデータを書き込み、Enter キーが押されるまで待ちます。
再生自体は、バッファの長さ分が終わると、無音になります。
無音状態になった後に Enter キーを押し、現在のステータスを出力します。
ステータス
高解像度のタイムスタンプである snd_htimestamp_t 型は、struct timespec と同じです。
tv_sec が秒単位、tv_nsec がナノ秒単位の値となります。
ソフトウェア構成で、タイムスタンプモードが NONE になっています。
"default" の場合は、この状態でもタイムスタンプは取得できるようですが、ハードウェアと直接通信する PCM で、タイムスタンプを取得したい場合は、タイムスタンプモードのセットが必要になります。
tv_sec が秒単位、tv_nsec がナノ秒単位の値となります。
ソフトウェア構成で、タイムスタンプモードが NONE になっています。
"default" の場合は、この状態でもタイムスタンプは取得できるようですが、ハードウェアと直接通信する PCM で、タイムスタンプを取得したい場合は、タイムスタンプモードのセットが必要になります。
- state は、RUNNING 状態です。
通常であればアンダーランの状態ですが、自動停止を無効にしているので、実行状態のままになっています。 - trigger は、トリガー時 (PCM が開始または停止した時) のタイムスタンプです。
14144 sec 873074695 ns となっていますが、これは、ALSA が起動した時、つまり PC が起動してから経過した時間となっています。 - tstamp は、現在のタイムスタンプです。
- audio は、オーディオのタイムスタンプです。
1 sec 565817458 ns となっているので、再生を開始してからの時間となっています。 - driver は、ドライバによって、タイムスタンプが生成された時間です。
tstamp よりも少し後の時間になっています。 - delay は、書き込み済みの終端位置から、再生位置を引いた値 (フレーム単位) になります。
バッファにある、未再生のデータのフレーム数です。
現在はアンダーラン状態なので、負の値になっています。 - avail は、バッファに読み書きできるフレーム数です。
avail + delay = 74992 - 58608 = 16384 になり、ちょうどバッファの長さと一致します。
つまり、avail の値は、bufsize - delay で計算されています。
リンクバッファ
書き込んだデータの後は、再生が無音になることと、boundary が大きな値であることから考えると、ハードウェア構成で設定されたバッファの長さと、実際のリングバッファの長さは、異なることがわかります。
ハードウェア構成のバッファの長さは、読み書きのためのバッファの長さであって、サウンドのリンクバッファは、ほぼ無限の長さであると考えることができます。
(実際にそれだけのバッファが確保されているわけではなく、表面上はそうなるように実装されている、ということだと思われます)
バッファの長さ分を書き込んでも、そのデータで無限ループになるわけではありません。
ハードウェア構成のバッファの長さは、読み書きのためのバッファの長さであって、サウンドのリンクバッファは、ほぼ無限の長さであると考えることができます。
(実際にそれだけのバッファが確保されているわけではなく、表面上はそうなるように実装されている、ということだと思われます)
バッファの長さ分を書き込んでも、そのデータで無限ループになるわけではありません。