ハードウェア構成のセット
ハードウェア構成に対して、各項目ごとのセット関数を使うと、一つの値または複数の値をセット(制限)できます。
ハードウェア構成内に、指定した単一の値のみをセットしたい場合は、以下のような関数を使います。
ただし、その PCM で利用できない値はセットできないので、その場合は負のエラーコードが返ります。
関数の数が多いので、詳しくは以下をご覧ください。
ハードウェア構成(1)
ハードウェア構成(2)
ハードウェア構成(3) - ピリオド
ハードウェア構成(4) - バッファ
ほとんどの項目では、有効な値を列挙できないので、指定した値に近いものだけを残したり、最小値を指定して、それ未満の値を除外したり、最大値を指定して、それを超える値を除外したりするなどして、ハードウェア構成内の値を制限します。
現在のハードウェア構成を削除し、リソースを解放します。
ハードウェア構成内に、指定した単一の値のみをセットしたい場合は、以下のような関数を使います。
//アクセスタイプをセット int snd_pcm_hw_params_set_access(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t access); //フォーマットをセット int snd_pcm_hw_params_set_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t format); //サブフォーマットをセット int snd_pcm_hw_params_set_subformat(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_subformat_t subformat);
ただし、その PCM で利用できない値はセットできないので、その場合は負のエラーコードが返ります。
関数の数が多いので、詳しくは以下をご覧ください。
ハードウェア構成(1)
ハードウェア構成(2)
ハードウェア構成(3) - ピリオド
ハードウェア構成(4) - バッファ
ほとんどの項目では、有効な値を列挙できないので、指定した値に近いものだけを残したり、最小値を指定して、それ未満の値を除外したり、最大値を指定して、それを超える値を除外したりするなどして、ハードウェア構成内の値を制限します。
ハードウェア構成のインストール
ハードウェア構成が準備できたら、PCM にインストールします。
params で設定されている項目の、「最初のアクセスタイプ、最初のフォーマット、最初のサブフォーマット、最小のチャンネル数、最小のサンプルレート、最小のピリオド時間、最大のバッファサイズ」を選択して、params を一つの値に更新し、構成をインストールします。
int snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params);
params で設定されている項目の、「最初のアクセスタイプ、最初のフォーマット、最初のサブフォーマット、最小のチャンネル数、最小のサンプルレート、最小のピリオド時間、最大のバッファサイズ」を選択して、params を一つの値に更新し、構成をインストールします。
ハードウェア構成の削除
int snd_pcm_hw_free(snd_pcm_t *pcm);
現在のハードウェア構成を削除し、リソースを解放します。
プログラム
「16bit LE、48000 Hz (に近い値)、最小のチャンネル数、他はデフォルト」でハードウェア構成をインストールした後、実際のハードウェア構成の情報を出力します。
$ cc -o 11-hwparam2 11-hwparam2.c -lasound
#include <stdio.h> #include <alsa/asoundlib.h> int main(int argc,char **argv) { snd_pcm_t *pcm; snd_pcm_hw_params_t *hw; snd_pcm_access_t access; snd_pcm_format_t format; snd_pcm_subformat_t subformat; unsigned int num,den,val; int dir; snd_pcm_uframes_t frames; if(snd_pcm_open(&pcm, (argc >= 2)? argv[1]: "default", SND_PCM_STREAM_PLAYBACK, 0)) { printf("open error\n"); return 1; } snd_pcm_hw_params_malloc(&hw); snd_pcm_hw_params_any(pcm, hw); //16bit LE snd_pcm_hw_params_set_format(pcm, hw, SND_PCM_FORMAT_S16_LE); //48000 Hz に近い値 val = 48000; dir = 0; snd_pcm_hw_params_set_rate_near(pcm, hw, &val, &dir); snd_pcm_hw_params(pcm, hw); // snd_pcm_hw_params_get_access(hw, &access); printf("access: %s\n", snd_pcm_access_name(access)); snd_pcm_hw_params_get_format(hw, &format); printf("format: %s\n", snd_pcm_format_name(format)); snd_pcm_hw_params_get_subformat(hw, &subformat); printf("subformat: %s\n", snd_pcm_subformat_name(subformat)); snd_pcm_hw_params_get_rate_numden(hw, &num, &den); printf("rate: %u/%u\n", num, den); snd_pcm_hw_params_get_channels(hw, &val); printf("channels: %u\n", val); printf("sbits: %d\n", snd_pcm_hw_params_get_sbits(hw)); printf("FIFO size: %d\n", snd_pcm_hw_params_get_fifo_size(hw)); snd_pcm_hw_params_get_min_align(hw, &frames); printf("min align: %lu frames\n", frames); // snd_pcm_hw_params_get_period_time(hw, &val, &dir); printf("\nperiod time: %u us, dir:%d\n", val, dir); snd_pcm_hw_params_get_period_size(hw, &frames, &dir); printf("period size: %lu frames, dir:%d\n", frames, dir); snd_pcm_hw_params_get_periods(hw, &val, &dir); printf("periods: %u, dir:%d\n", val, dir); snd_pcm_hw_params_get_buffer_time(hw, &val, &dir); printf("buffer time: %u us, dir:%d\n", val, dir); snd_pcm_hw_params_get_buffer_size(hw, &frames); printf("buffer size: %lu frames\n", frames); // printf("\ncan_mmap_sample_resolution: %d\n", snd_pcm_hw_params_can_mmap_sample_resolution(hw)); printf("is_double: %d\n", snd_pcm_hw_params_is_double(hw)); printf("is_batch: %d\n", snd_pcm_hw_params_is_batch(hw)); printf("is_block_transfer: %d\n", snd_pcm_hw_params_is_block_transfer(hw)); printf("is_monotonic: %d\n", snd_pcm_hw_params_is_monotonic(hw)); printf("can_overrange: %d\n", snd_pcm_hw_params_can_overrange(hw)); printf("can_pause: %d\n", snd_pcm_hw_params_can_pause(hw)); printf("can_resume: %d\n", snd_pcm_hw_params_can_resume(hw)); printf("is_half_duplex: %d\n", snd_pcm_hw_params_is_half_duplex(hw)); printf("is_joint_duplex: %d\n", snd_pcm_hw_params_is_joint_duplex(hw)); printf("can_sync_start: %d\n", snd_pcm_hw_params_can_sync_start(hw)); printf("can_disable_period_wakeup: %d\n", snd_pcm_hw_params_can_disable_period_wakeup(hw)); printf("is_perfect_drain: %d\n", snd_pcm_hw_params_is_perfect_drain(hw)); printf("supports_audio_wallclock_ts: %d\n", snd_pcm_hw_params_supports_audio_wallclock_ts(hw)); // snd_pcm_hw_params_free(hw); snd_pcm_close(pcm); return 0; }
実行結果
access: MMAP_INTERLEAVED format: S16_LE subformat: STD rate: 48000/1 channels: 1 sbits: 16 FIFO size: 0 min align: 1 frames period time: 21333 us, dir:1 period size: 1024 frames, dir:0 periods: 16, dir:0 buffer time: 341333 us, dir:1 buffer size: 16384 frames can_mmap_sample_resolution: 0 is_double: 0 is_batch: 0 is_block_transfer: 1 is_monotonic: 1 can_overrange: 0 can_pause: 0 can_resume: 0 is_half_duplex: 0 is_joint_duplex: 0 can_sync_start: 1 can_disable_period_wakeup: 1 is_perfect_drain: 0 supports_audio_wallclock_ts: 1
アクセスタイプは MMAP_INTERLEAVED です。
前回の出力時、最初のタイプは MMAP_INTERLEAVED だったので、最初のアクセスタイプが選択されています。
サブフォーマットも同様です。
サンプルレートは、48000 Hz になっています。
チャンネル数は、一番小さいチャンネル数が選択されるので、1 (モノラル) です。
ピリオドの詳細は後述しますが、サウンドバッファは小さい塊の単位に分割され、その1単位をピリオドと呼びます。
バッファは、ピリオド単位のサイズになります。
ピリオド
ピリオドの長さは、「マイクロ秒単位の時間」「サンプルのフレーム単位」のいずれかで指定できます。period time は 21333 us、period size は 1024 frames です。
48000 Hz における 1024 フレームを、以下の式で時間に変換すると、21333.333... マイクロ秒となり、period time の値と一致します。
time(us) = frames / samprate(Hz) * 1000 * 1000
小数点以下の値があるので、period time の dir は 1 になっています。
dir は、計算の結果、小数点以下の値がある場合、実際の正確な値が、返った整数値のどの方向にあるかという値です (-1, 0, 1)。
21333.333 が 21333 に切り捨てられて返っているので、実際の正確な値は、正 (1) の方向にあります。
バッファ
ALSA のサウンドバッファは、リングバッファになっています。ハードウェア構成で指定するバッファのサイズは、リングバッファの長さではなく、読み書きに使用するためのバッファのサイズとなります。
バッファのサイズは、「ピリオドの数」「マイクロ秒単位の時間」「サンプルのフレーム単位」のいずれかで指定できます。
ただし、実際はピリオド単位のサイズになります。
periods は 16 なので、ちょうどピリオド 16 個分の長さになっています。
ピリオドのフレーム数は 1024 なので、1024 * 16 = 16384 で、buffer size のフレーム数の値と一致します。
バッファの長さを時間に変換すると、16384/48000*1000*1000 = 341333.333... us で、buffer time の値と一致します。
HDMI ほか
$ ./11-hwparam2 hdmi access: MMAP_INTERLEAVED format: S16_LE subformat: STD rate: 48000/1 channels: 2 sbits: 16 FIFO size: 0 min align: 1 frames period time: 682666 us, dir:1 period size: 32768 frames, dir:0 periods: 32, dir:0 buffer time: 21845333 us, dir:1 buffer size: 1048576 frames can_mmap_sample_resolution: 1 is_double: 0 is_batch: 0 is_block_transfer: 1 is_monotonic: 1 can_overrange: 0 can_pause: 1 can_resume: 0 is_half_duplex: 0 is_joint_duplex: 0 can_sync_start: 1 can_disable_period_wakeup: 1 is_perfect_drain: 0 supports_audio_wallclock_ts: 1
HDMI の場合も、結果はほぼ同じですが、ピリオド・バッファサイズは default よりもかなり大きくなっています。
"front" や "hw" でも同じ結果となります。
"default" の場合は、サンプルの変換が有効なので、ソフトウェア的な関与がありますが、"hdmi" などはハードウェアと直接通信する形なので、若干値が異なります。
ここでは、以下の値が 1 になっています。
can_mmap_sample_resolution
can_pause
can_pause が 1 の場合は、再生の一時停止/再開がサポートされています。
"default" の場合は 0 なので、再生の一時停止はできません。