[RK3288][Android6.0] 除錯筆記 — 修改錄音取樣率提示報錯問題

[RK3288][Android6.0] 除錯筆記 — 修改錄音取樣率提示報錯問題

Platform: Rockchip
OS: Android 6.0
Kernel: 3.10.92

需求
由於app取樣率設定的是16k,而硬體用的是44.1kHz,為了避免重取樣帶來的損耗,因此將HAL層改了16kHz,logcat提示如下error:
01-21 23:01:23.047   210  1207 E AudioHardwareTiny: pcm_open() failed: cannot set hw params: Invalid argument

除錯
呼叫的流程是
pcm_open -> pcm.c
    ioctl -> //cmd SNDRV_PCM_IOCTL_HW_PARAMS
        snd_pcm_capture_ioctl1 ->
            snd_pcm_common_ioctl1 ->
                snd_pcm_hw_params_user ->
                    snd_pcm_hw_params ->
                        snd_pcm_hw_refine ->
                            snd_interval_refine ->
                                snd_interval_checkempty
跑進snd_interval_checkempty()說明傳進來的值在經過計算後不符合要求,所以成了empty。往回看snd_interval_refine()的第二個引數constrs_interval(constrs, k)

static inline struct snd_interval *constrs_interval(struct snd_pcm_hw_constraints *constrs,
snd_pcm_hw_param_t var)
{
return &constrs->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL];
}

k即SNDRV_PCM_HW_PARAM_RATE, constrs即substream->runtime->hw_constraints,找到對hw_constraints中的設定是在open的時候
snd_pcm_capture_open ->
    snd_pcm_open ->
        snd_pcm_open_substream ->
            soc_pcm_open ->
                soc_pcm_apply_symmetry ->
                    snd_pcm_hw_constraint_minmax    //注意這裡的最後兩個引數都是soc_dai->rate
                        snd_interval_refine    //最後把這兩個引數更新到hw_constraints中rate對應的interval中去
soc_pcm_apply_symmetry()需要拿出來說下

static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,
struct snd_soc_dai *soc_dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
int ret;
if (!soc_dai->driver->symmetric_rates &&
!rtd->dai_link->symmetric_rates)
return 0;
......
dev_info(soc_dai->dev, "ASoC: Symmetry forces %dHz rate\n", soc_dai->rate);
//使用soc_dai->rate作為預設的取樣率,這裡有可能是cpu dai的也有可能是codec dai的rate
ret = snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_RATE,
soc_dai->rate, soc_dai->rate);
......
}

這裡有個很重要的判斷,判斷soc_dai->driver->symmetric_rates或者rtd->dai_link->symmetric_rates是不是有設定,那麼symmetric_rates有什麼意義,摘錄兩段commit:

簡單地說,就是由於audio codec由於硬體限制,而當你又要同時使用playback和capture的時候,它是無法同時輸出兩個不同的clock來滿足不同的播放和錄音取樣率,所以當你使用其中一個substream的時候,另一個type的substream的取樣率就會受到限制。

可以看到,rk_i2s.c也就是平臺的cpu dai也做了限制:
static struct snd_soc_dai_driver rockchip_i2s_dai = {
……
    .ops = &rockchip_i2s_dai_ops,
    .symmetric_rates = 1,
};
那麼上面的soc_dai->rate是在哪裡設定的呢?我也是恍然大悟才想起來錄音的同時先開啟了playback stream,此值是在開啟播放的時候設定的,困擾了我好一會,尷尬…
也正由於這個機制,之前我用tinycap直接驗證錄音也沒發現此問題就提交了程式碼.
因此, 如果不是同時使用的話,那麼取樣率是可以修改的。

參考:
https://searchcode.com/codesearch/view/30197122/
https://git.congatec.com/arm/qmx6_kernel/commit/7912f03c2b2a0c43eae9b758fa768796fa9095d3