Raspberry Pi ZeroのI2S DACとポップノイズの関係 その1 力技でポップノイズを抑制

概要

  • NosPiDAC Zeroなどで音楽を楽しんでいるときに気になるポップノイズについて調べたこと・やったことをメモ。

  • ポップノイズは再生・停止・曲間で発生します。

  • 発生メカニズムが確定では無いですが、RPi ZeroのI2SドライバがI2Sのクロックを止めることから発生しているようです。
    • 2018/07/31追記 クロックだけではなくTXONも影響してそう
  • 本内容は暫定対処です。もっと良い対応方法が無いか引き続き研究中です。

I2Sクロック制御

ポップノイズ発生シーケンスおさらい

  • ALSAからのcallbackを追っていきます
    1. 再生開始
    2. startup PCMブロックのEnable
    3. hw_params サンプリングレートを設定・ここでPLLやLRCLKの分周比を設定
    4. prepare FIFOクリア
    5. trigger start 一曲目再生 クロックオン ★ここでプチ音
    6. ここで曲送り
    7. trigger stop 一曲目停止 クロックオフ
    8. prepare FIFOクリア
    9. trigger start 二曲目再生 クロックオン ★ここでプチ音
    10. ここで再生停止
    11. trigger stop 二曲目停止 クロックオフ
    12. shutdown PCMブロックのDisable/クロックオフ
    13. 再生再開
    14. trigger stop 空うち
    15. startup PCMブロックのEnable
    16. hw_params サンプリングレートを設定・ここでPLLやLRCLKの分周比を設定
    17. prepare FIFOクリア
    18. trigger start 二曲目再生再開 クロックオン ★ここでプチ音
    19. trigger stop 二曲目停止 クロックオフ
    20. shutdown PCMブロックのDisable/クロックオフ

クロックを止まらないようにしてみる

  • そんなわけで、I2Sがクロックを止めようとする制御を全部コメントアウトしてみる。
  • ポイントはbcm2835_i2s_hw_paramsでTXON時にはreturn 0する処理をつぶすこと。これをやらないと、サンプリングレートが変わってもクロックを変更してくれません。
  • デメリットは再生停止してもクロックを供給し続けるので、再生停止時の消費電力が上がります。
  • また、TXON状態のままになるので、途中で録音しようとしてもエラーになりそう。
  • 再生専用機なら問題は少ないかな?
  • 現在このコードがじんそんさんのNosPiDAC Zero用 Moode Audioに適用されています。
diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c
index d5f73a8ab893..b2773fb1ce20 100644
--- a/sound/soc/bcm/bcm2835-i2s.c
+++ b/sound/soc/bcm/bcm2835-i2s.c
@@ -357,8 +357,8 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
         */
        regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &csreg);

-       if (csreg & (BCM2835_I2S_TXON | BCM2835_I2S_RXON))
-               return 0;
+//     if (csreg & (BCM2835_I2S_TXON | BCM2835_I2S_RXON))
+//             return 0;

        data_length = params_width(params);
        data_delay = 0;
@@ -652,17 +655,19 @@ static void bcm2835_i2s_stop(struct bcm2835_i2s_dev *dev,
 {
        uint32_t mask;

        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
                mask = BCM2835_I2S_RXON;
        else
                mask = BCM2835_I2S_TXON;

-       regmap_update_bits(dev->i2s_regmap,
-                       BCM2835_I2S_CS_A_REG, mask, 0);
+//     regmap_update_bits(dev->i2s_regmap,
+//                     BCM2835_I2S_CS_A_REG, mask, 0);

        /* Stop also the clock when not SND_SOC_DAIFMT_CONT */
-       if (!dai->active && !(dev->fmt & SND_SOC_DAIFMT_CONT))
-               bcm2835_i2s_stop_clock(dev);
+//     if (!dai->active && !(dev->fmt & SND_SOC_DAIFMT_CONT))
+//             bcm2835_i2s_stop_clock(dev);
 }
@@ -735,14 +742,14 @@ static void bcm2835_i2s_shutdown(struct snd_pcm_substream *substream,
                return;

        /* Disable the module */
-       regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG,
-                       BCM2835_I2S_EN, 0);
+//     regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG,
+//                     BCM2835_I2S_EN, 0);

        /*
         * Stopping clock is necessary, because stop does
         * not stop the clock when SND_SOC_DAIFMT_CONT
         */
-       bcm2835_i2s_stop_clock(dev);
+//     bcm2835_i2s_stop_clock(dev);
 }

改造後シーケンスおさらい

  • クロック停止処理削除
    1. 再生開始
    2. startup
    3. hw_param
    4. prepare
    5. trigger start 一曲目再生 クロックオン ★ここでプチ音
    6. ここで曲送り
    7. trigger stop 一曲目停止 クロック止めない
    8. prepare
    9. trigger start 二曲目再生
    10. ここで再生停止
    11. trigger stop 二曲目停止 クロック止めない
    12. shutdown クロック止めない
    13. 再生再開
    14. trigger stop 空うち
    15. startup PCMブロックのEnable
    16. hw_params サンプリングレートを設定・ここでPLLやLRCLKの分周比を設定
    17. prepare FIFOクリア
    18. trigger start 二曲目再生再開
    19. trigger stop 二曲目停止 クロック止めない
    20. shutdown クロック止めない

改造後シーケンス+rc.localで無音再生

無音再生の追加

  • 最初の曲で出るポップノイズを抑制するため、起動中に無音再生することでI2Sのクロック出力を開始する試み
  • SoXのplayコマンドを使って、400HzのSine波を-100dB(無音までゲインを下げる)で再生する処理を追加してます。

  • 以下をRPi MoodeAudio上で実行する

sudo apt update
sudo apt install sox
  • エディタでrc.localを開く
sudo vi /etc/rc.local
  • exit 0の前あたりに下記を追加
/usr/bin/play -n synth 1 sine 400 gain -100 &
  • SHIFT + ZZなどで保存して終了

シーケンス

  • 事前に無音音声再生&クロック停止削除
    1. 無音再生開始
    2. startup
    3. hw_param
    4. prepare
    5. trigger start 無音再生 クロックオン ★ここでプチ音(無音再生なので軽微)
    6. trigger stop 無音再生停止
    7. shutdown クロック止めない
    8. 再生開始
    9. trigger stop 空うち
    10. startup PCMブロックのEnable
    11. hw_params サンプリングレートを設定・ここでPLLやLRCLKの分周比を設定
    12. prepare
    13. trigger start 一曲目再生
    14. trigger stop 一曲目停止 クロック止めない
    15. prepare
    16. trigger start 二曲目再生
    17. trigger stop 二曲目停止 クロック止めない
    18. shutdown クロック止めない
    19. 再生再開
    20. trigger stop 空うち
    21. startup PCMブロックのEnable
    22. hw_params サンプリングレートを設定・ここでPLLやLRCLKの分周比を設定
    23. prepare FIFOクリア
    24. trigger start 二曲目再生再開
    25. trigger stop 二曲目停止 クロック止めない
    26. shutdown クロック止めない

備考

  • SND_SOC_DAIFMT_CONTでtrigger stopのクロック停止を抑制できそう。
  • ただ、このフラグを立てるのはhifiberry_dac.cを直さなきゃいけないので、1ファイルの差し替えで済むので現行のままがお手軽かも。