ALSA播放在没有snd_pcm_hw_params_get_ *调用的情况下中断

时间:2018-04-06 01:44:57

标签: audio alsa

我发现当我对snd_pcm_hw_params_get_*函数进行一些调用时,一个简单的ALSA回放程序的行为会有所不同。我的程序反复从缓冲区播放正弦波。当我加入电话时,我会得到一个纯正的音调。但是,当我删除电话时,我会收到一连串的哔哔声。这让我很担心,因为我不希望检索数据的调用与声音的播放方式有任何关系。我在廉价的USB声卡和我的(可能更好的)内置声卡上都有这种行为。

以下是代码:

#define GETPARAMS


int main() {
  snd_pcm_t *handle;
  snd_pcm_hw_params_t *params;
  const char name[] = "hw:0,0";

  int dir;

  snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
  snd_pcm_access_t access = SND_PCM_ACCESS_RW_INTERLEAVED;
  snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
  unsigned int rate = 48000;
  unsigned int channels = 2;
  unsigned int periods = 4;
  snd_pcm_uframes_t periodsize = 2048;
  int num_frames = 2*periodsize;


  snd_pcm_hw_params_alloca(&params);
  snd_pcm_open(&handle, name, stream, 0);
  snd_pcm_hw_params_any(handle, params);

#ifdef GETPARAMS
  printf("\nparameters before setting:\n");
  snd_pcm_hw_params_get_rate(params, &rate, &dir);
  printf("  rate = %d, dir = %d\n", rate, dir);
  snd_pcm_hw_params_get_channels(params, &channels);
  printf("  channels = %d\n", channels);
  snd_pcm_hw_params_get_periods(params, &periods, &dir);
  printf("  periods = %d, dir = %d\n", periods, dir);
  snd_pcm_hw_params_get_buffer_size(params, &periodsize);
  printf("  periodsize = %ld\n", periodsize);
#endif

  snd_pcm_hw_params_set_access(handle, params, access);
  snd_pcm_hw_params_set_format(handle, params, format);
  snd_pcm_hw_params_set_rate_near(handle, params, &rate, &dir);
  snd_pcm_hw_params_set_channels(handle, params, 2);
  snd_pcm_hw_params_set_periods(handle, params, periods, 0);
  snd_pcm_hw_params_set_buffer_size(handle, params, num_frames);
  snd_pcm_hw_params(handle, params);

#ifdef GETPARAMS
  printf("\nparameters after setting:\n");
  snd_pcm_hw_params_get_rate(params, &rate, &dir);
  printf("  rate = %d, dir = %d\n", rate, dir);
  snd_pcm_hw_params_get_channels(params, &channels);
  printf("  channels = %d\n", channels);
  snd_pcm_hw_params_get_periods(params, &periods, &dir);
  printf("  periods = %d, dir = %d\n", periods, dir);
  snd_pcm_hw_params_get_buffer_size(params, &periodsize);
  printf("  periodsize = %ld\n\n", periodsize);
#endif

  int16_t *data = (int16_t*)calloc(2*periodsize, sizeof(int16_t));
  loadpage(data, 2*periodsize);

  snd_pcm_sframes_t frames;
  snd_pcm_prepare(handle);
  for (int i=0; i<8; i++) {
    frames = snd_pcm_writei(handle, data, num_frames);
    if (frames < 0)
      frames = snd_pcm_recover(handle, frames, 0);
    if (frames < 0) {
      printf("snd_pcm_writei failed: %s\n", snd_strerror(frames));
    }
    if (frames > 0 && frames < num_frames)
      printf("short write (expected %d, write %li)\n", num_frames, frames);
  }

  snd_pcm_close(handle);
  free(data);
}

loadpage()填充缓冲区。当我发表评论#define GETPARAMS时,我会收到一连串的哔哔声。当我加入它时,我会得到一个纯净的音调。

以下是定义GETPARAMS时的输出:

parameters before setting:
  rate = 48000, dir = 32766
  channels = 2
  periods = 4, dir = 32766
  periodsize = 2048

parameters after setting:
  rate = 48000, dir = 0
  channels = 2
  periods = 4, dir = 0
  periodsize = 4096

1 个答案:

答案 0 :(得分:0)

如果尚未设置参数,则不得调用snd_pcm_hw_param_get_*()函数,因为此时配置空间包含参数的多个潜在值。

要打印hw_params容器的当前状态,请使用snd_pcm_hw_params_dump()

snd_output_t *output;

snd_output_stdio_attach(&output, stdout, 0);

...

snd_pcm_hw_params_dump(params, output);

...

snd_output_close(output);

无论如何,问题是periodsperiodsizenum_frames的初始值不一致,并且_get_调用用其他值覆盖这些变量碰巧是一致的。

我不知道你真正想要使用什么值,但请注意,周期大小和缓冲区大小是以帧为单位测量的,并且一帧包含所有通道的所有样本,即在这种情况下,一帧具有四个字节。