カラクリスタ

ただの「人」の個人ブログ。人です

NixOS + Pipewire でサンプリングパーフェクト再生をやっていく

ビットパーフェクト再生ではなくサンプリングレートパーフェクト再生なら出来たので、 そこの所をメモっておきます。

前提

  • NixOSを使っている
  • Pipewireを音の再生に使っている

どうやってやったか

これは設定ファイルを出した方が早いので出します:

参照

nixos-configurations/system/config/audio/pipewire.nix at dcebcddcb2ae67a1b5b8c038859c742ea78b85bd · nyarla/nixos-configurations

設定の抜粋と解説

{ pkgs, ... }:
{
  # pipewire と rtkit を有効にする
  security.rtkit.enable = true;
  services.pipewire = {
    enable = true;

    # 各種オーディオバックエンドを有効に
    alsa.enable = true;
    alsa.support32Bit = true;
    pulse.enable = true;
    jack.enable = true;

    # wireplumber を有効にする
    # これが無効だと再生デバイスの記憶がされない
    wireplumber = {
      enable = true;
    };

    # ここからが本番
    extraConfig = {
      pipewire = {
        # まず Pipewire で受け入れるサンプリングレートを設定する
        # 私の場合 Focusrite Scarlette Solo 2 Gen を使っているので、それに合わせている
        # なお再生デバイスがサポートしているビットレートは `cat /proc/asound/*/streamX` で確認できる
        "10-clock-rate" = {
          "context.properties" = {
            "default.clock.allowed-rates" = [
              44100
              48000
              88200
              96000
              176400
              192000
            ];
          };
        };

        # ここでオーディオインターフェースの設定をする
        # この設定は Focusrite Scarlette Solo 2 Gen のもの
        "98-focusrite-scalette-2gen" = {
          "device.rules" = [
            {
              matches = [
                { "device.name" = "alsa_card.usb-Focusrite_Scarlett_Solo_USB-00"; }
              ];
              actions = {
                update-props = {
                  # alsa で認識する Audio Format を設定する
                  # この場合 `/proc/asound/*/streamX` では `S32_LE` と出たので、そのように設定
                  "alsa.format" = "S32_LE";
                  "audio.format" = "S32LE";
                };
              };
            }
          ];
        };
      };

      # **ここが最大のポイント**
      # `stream.properties` で `resample.disable = true;` を設定するとリサンプリングが無効となるので、
      # 常にサンプリングレートパーフェクトな再生が行われるようになる
      #
      # ただしビット数を完全に合わせるためには、音源の再生毎にBit数を変更しなければならないので、
      # 音楽再生専用機などでBit perfect再生をしようと思うとスクリプトを組まないと多分できない
      client = {
        "10-no-resampling" = {
          "stream.properties" = {
            "resample.disable" = true;
          };
        };
      };
      client-rt = {
        "10-no-resampling" = {
          "stream.properties" = {
            "resample.disable" = true;
          };
        };
      };
      pipewire-pulse = {
        "10-no-resampling" = {
          "stream.properties" = {
            "resample.disable" = true;
          };
        };
      };
    };
  };
}

確認方法

音楽などを再生した状態で、

$ cat /proc/asound/XXX/pcm0p/sub0/hw_params

を実行。その出力として

access: MMAP_INTERLEAVED
format: S32_LE
subformat: STD
channels: 2
rate: 48000 (48000/1)
period_size: 512
buffer_size: 32768

のように rate の値が音源ファイルと一致していればOKで、合っていなければ設定が上手く行えていません。

補足

上記の設定はPipewireでのサンプリングレートパーフェクト再生が出来ます。

ただしこれはPipewireが出力するサンプリングレートを音源と完全に合わせる、 という手段であるため、仮にPipewireが常にリサンプリングを行う仕様であった場合、 ビットパーフェクトの状態に持っていくことは不可能となります。

またここからビットパーフェクト再生へ持っていこうと思うと、

  1. 再生中(or再生予定)の音源ファイルのビットレートを取得する
  2. 音源の再生前に pwctlpw-clialsa.resolution_bitsを変更する
  3. 上記の2つを動的なスクリプトとして実装する

と言う手段が必要となるため、常にビットパーフェクト再生をしたい場合、 スクリプト処理の可能な音楽プレイヤーが必須となります。

最後にこの手段でビットパーフェクト再生が可能なのか、については、 確認のため光デジタル出力で出力されたデータと音源ファイルの突き合せが必要で、 私の方ではそういった機器の持ち合せがないため検証していません。

注意点

上記設定はリサンプリングを封じていることに由来して、 各アプリケーション間での再生切り替えに支障が出る時もあります。

その場合、単一のアプリケーションだけで音源再生をするか、 もしくはアプリケーションを再起動することで問題を回避できますが、 その点については留意しておいてください。

という事で今回の記事は以上です。