【问题标题】:ALSA playback interrupted without snd_pcm_hw_params_get_* callsALSA 播放中断,没有 snd_pcm_hw_params_get_* 调用
【发布时间】:2018-04-07 14:25:44
【问题描述】:

我发现当我调用 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

【问题讨论】:

  • 每次函数调用后检查错误。
  • 我有。在发布之前,我取出了检查以减小代码的大小。即使我有错误或没有错误,我仍然感到困惑。为什么代码的行为要依赖于设置参数前后获取数据?

标签: audio alsa


【解决方案1】:

如果尚未设置参数,则不得调用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_调用会用其他恰好一致的值覆盖这些变量。

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

【讨论】:

    猜你喜欢
    • 2013-08-04
    • 2013-10-28
    • 2011-04-25
    • 2016-02-10
    • 2018-05-12
    • 1970-01-01
    • 2016-05-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多