How to add a Stereo Audio Profile?

Hey,

I would like to use my sound/audio device “Scarlett 4i4 USB Pro” with a custom profile/configuration. The reason is that it currently only provides Surround 4.0 Output, but I only have two front speakers or headphones connected. This means applications try to play audio on rear speakers that don’t exist. In games that means I can’t hear anything behind me.

Ideally I need a “Analog Stereo 2.0 Output + Analog Mono 1.0 Input” profile. How can I achieve this?

So far I have tried creating custom UCM2 and WirePlumber profiles without success. Also running pw-cli set-param 96 Props '{ "audio.channels": "2", "audio.position": "FL,FR" }' had no effect.

Output of pw-dump for the relevant sink device:

[
  {
    "id": 96,
    "type": "PipeWire:Interface:Node",
    "version": 3,
    "permissions": [ "r", "w", "x", "m" ],
    "info": {
      "max-input-ports": 65,
      "max-output-ports": 0,
      "change-mask": [ "input-ports", "output-ports", "state", "props", "params" ],
      "n-input-ports": 4,
      "n-output-ports": 4,
      "state": "suspended",
      "error": null,
      "props": {
        "alsa.card": 0,
        "alsa.card_name": "Scarlett 4i4 USB",
        "alsa.class": "generic",
        "alsa.components": "USB1235:8212",
        "alsa.device": 0,
        "alsa.driver_name": "snd_usb_audio",
        "alsa.id": "USB Audio",
        "alsa.long_card_name": "Focusrite Scarlett 4i4 USB at usb-0000:0d:00.0-3.1, high speed",
        "alsa.mixer_name": "USB Mixer",
        "alsa.name": "USB Audio",
        "alsa.resolution_bits": 32,
        "alsa.subclass": "generic-mix",
        "alsa.subdevice": 0,
        "alsa.subdevice_name": "subdevice #0",
        "alsa.sync.id": "00000000:00000000:00000000:00000000",
        "api.alsa.card.longname": "Focusrite Scarlett 4i4 USB at usb-0000:0d:00.0-3.1, high speed",
        "api.alsa.card.name": "Scarlett 4i4 USB",
        "api.alsa.path": "surround40:0",
        "api.alsa.pcm.card": 0,
        "api.alsa.pcm.stream": "playback",
        "audio.channels": 4,
        "audio.position": "FL,FR,RL,RR",
        "card.profile.device": 6,
        "client.id": 74,
        "clock.quantum-limit": 8192,
        "device.api": "alsa",
        "device.bus": "usb",
        "device.class": "sound",
        "device.icon-name": "audio-card-analog",
        "device.id": 89,
        "device.profile.description": "Analog Surround 4.0",
        "device.profile.name": "analog-surround-40",
        "device.routes": 1,
        "factory.id": 19,
        "factory.name": "api.alsa.pcm.sink",
        "library.name": "audioconvert/libspa-audioconvert",
        "media.class": "Audio/Sink",
        "node.description": "Scarlett 4i4 USB Analog Surround 4.0",
        "node.driver": true,
        "node.loop.name": "data-loop.0",
        "node.name": "alsa_output.usb-Focusrite_Scarlett_4i4_USB_D8CTQ2Y9A0B20D-00.analog-surround-40",
        "node.nick": "Scarlett 4i4 USB",
        "node.pause-on-idle": false,
        "object.id": 96,
        "object.path": "alsa:acp:USB:6:playback",
        "object.serial": 1145,
        "port.group": "playback",
        "priority.driver": 1109,
        "priority.session": 1109
      },
      "params": {
        "EnumFormat": [
          {
            "mediaType": "audio",
            "mediaSubtype": "raw",
            "format": "S32LE",
            "rate": { "default": 48000, "min": 44100, "max": 192000 },
            "channels": 4,
            "position": [ "FL", "FR", "RL", "RR" ]
          }
        ],
        "PropInfo": [
          {
            "id": "volume",
            "description": "Volume",
            "type": { "default": 1.000000, "min": 0.000000, "max": 10.000000 }
          },
          {
            "id": "mute",
            "description": "Mute",
            "type": {
              "default": false,
              "alt1": false,
              "alt2": true
            }
          },
          {
            "id": "channelVolumes",
            "description": "Channel Volumes",
            "type": { "default": 1.000000, "min": 0.000000, "max": 10.000000 },
            "container": "Array"
          },
          {
            "id": "channelMap",
            "description": "Channel Map",
            "type": "",
            "container": "Array"
          },
          {
            "id": "monitorMute",
            "description": "Monitor Mute",
            "type": {
              "default": false,
              "alt1": false,
              "alt2": true
            }
          },
          {
            "id": "monitorVolumes",
            "description": "Monitor Volumes",
            "type": { "default": 1.000000, "min": 0.000000, "max": 10.000000 },
            "container": "Array"
          },
          {
            "id": "softMute",
            "description": "Soft Mute",
            "type": {
              "default": false,
              "alt1": false,
              "alt2": true
            }
          },
          {
            "id": "softVolumes",
            "description": "Soft Volumes",
            "type": { "default": 1.000000, "min": 0.000000, "max": 10.000000 },
            "container": "Array"
          },
          {
            "name": "monitor.channel-volumes",
            "description": "Monitor channel volume",
            "type": {
              "default": false,
              "alt1": false,
              "alt2": true
            },
            "params": true
          },
          {
            "name": "channelmix.disable",
            "description": "Disable Channel mixing",
            "type": {
              "default": false,
              "alt1": false,
              "alt2": true
            },
            "params": true
          },
          {
            "name": "channelmix.min-volume",
            "description": "Minimum volume level",
            "type": { "default": 0.000000, "min": 0.000000, "max": 10.000000 },
            "params": true
          },
          {
            "name": "channelmix.max-volume",
            "description": "Maximum volume level",
            "type": { "default": 10.000000, "min": 0.000000, "max": 10.000000 },
            "params": true
          },
          {
            "name": "channelmix.normalize",
            "description": "Normalize Volumes",
            "type": {
              "default": false,
              "alt1": false,
              "alt2": true
            },
            "params": true
          },
          {
            "name": "channelmix.mix-lfe",
            "description": "Mix LFE into channels",
            "type": {
              "default": true,
              "alt1": true,
              "alt2": false
            },
            "params": true
          },
          {
            "name": "channelmix.upmix",
            "description": "Enable upmixing",
            "type": {
              "default": true,
              "alt1": true,
              "alt2": false
            },
            "params": true
          },
          {
            "name": "channelmix.lfe-cutoff",
            "description": "LFE cutoff frequency",
            "type": { "default": 0.000000, "min": 0.000000, "max": 1000.000000 },
            "params": true
          },
          {
            "name": "channelmix.fc-cutoff",
            "description": "FC cutoff frequency (Hz)",
            "type": { "default": 0.000000, "min": 0.000000, "max": 48000.000000 },
            "params": true
          },
          {
            "name": "channelmix.rear-delay",
            "description": "Rear channels delay (ms)",
            "type": { "default": 0.000000, "min": 0.000000, "max": 1000.000000 },
            "params": true
          },
          {
            "name": "channelmix.stereo-widen",
            "description": "Stereo widen",
            "type": { "default": 0.000000, "min": 0.000000, "max": 1.000000 },
            "params": true
          },
          {
            "name": "channelmix.hilbert-taps",
            "description": "Taps for phase shift of rear",
            "type": { "default": 0, "min": 0, "max": 255 },
            "params": true
          },
          {
            "name": "channelmix.upmix-method",
            "description": "Upmix method to use",
            "type": "none",
            "params": true,
            "labels": [
              "none",
              "Disabled",
              "simple",
              "Simple upmixing",
              "psd",
              "Passive Surround Decoding"
            ]
          },
          {
            "id": "rate",
            "description": "Rate scaler",
            "type": { "default": 1.000000, "min": 0.000000, "max": 10.000000 }
          },
          {
            "id": "quality",
            "name": "resample.quality",
            "description": "Resample Quality",
            "type": { "default": 4, "min": 0, "max": 14 },
            "params": true
          },
          {
            "name": "resample.disable",
            "description": "Disable Resampling",
            "type": {
              "default": false,
              "alt1": false,
              "alt2": true
            },
            "params": true
          },
          {
            "name": "dither.noise",
            "description": "Add noise bits",
            "type": { "default": 0, "min": 0, "max": 16 },
            "params": true
          },
          {
            "name": "dither.method",
            "description": "The dithering method",
            "type": "none",
            "params": true,
            "labels": [
              "none",
              "Disabled",
              "rectangular",
              "Rectangular dithering",
              "triangular",
              "Triangular dithering",
              "triangular-hf",
              "Sloped Triangular dithering",
              "wannamaker3",
              "Wannamaker 3 dithering",
              "shaped5",
              "Lipshitz 5 dithering"
            ]
          },
          {
            "name": "debug.wav-path",
            "description": "Path to WAV file",
            "type": "",
            "params": true
          },
          {
            "name": "channelmix.lock-volumes",
            "description": "Disable volume updates",
            "type": {
              "default": false,
              "alt1": false,
              "alt2": true
            },
            "params": true
          },
          {
            "name": "audioconvert.filter-graph.disable",
            "description": "Disable Filter graph updates",
            "type": {
              "default": false,
              "alt1": false,
              "alt2": true
            },
            "params": true
          },
          {
            "name": "audioconvert.filter-graph",
            "description": "A filter graph to load",
            "type": "",
            "params": true
          },
          {
            "id": "device",
            "name": "api.alsa.path",
            "description": "The ALSA device",
            "type": "surround40:0"
          },
          {
            "id": "deviceName",
            "description": "The ALSA device name",
            "type": ""
          },
          {
            "id": "cardName",
            "description": "The ALSA card name",
            "type": ""
          },
          {
            "id": "latencyOffsetNsec",
            "description": "Latency offset (ns)",
            "type": { "default": 0, "min": 0, "max": 2000000000 }
          },
          {
            "name": "audio.channels",
            "description": "Audio Channels",
            "type": 4,
            "params": true
          },
          {
            "name": "audio.rate",
            "description": "Audio Rate",
            "type": 0,
            "params": true
          },
          {
            "name": "audio.format",
            "description": "Audio Format",
            "type": "UNKNOWN",
            "params": true
          },
          {
            "name": "audio.position",
            "description": "Audio Position",
            "type": "[ FL, FR, RL, RR ]",
            "params": true
          },
          {
            "name": "audio.allowed-rates",
            "description": "Audio Allowed Rates",
            "type": "[  ]",
            "params": true
          },
          {
            "name": "api.alsa.period-size",
            "description": "Period Size",
            "type": { "default": 0, "min": 0, "max": 8192 },
            "params": true
          },
          {
            "name": "api.alsa.period-num",
            "description": "Number of Periods",
            "type": { "default": 0, "min": 0, "max": 1024 },
            "params": true
          },
          {
            "name": "api.alsa.headroom",
            "description": "Headroom",
            "type": { "default": 0, "min": 0, "max": 8192 },
            "params": true
          },
          {
            "name": "api.alsa.start-delay",
            "description": "Start Delay",
            "type": { "default": 0, "min": 0, "max": 8192 },
            "params": true
          },
          {
            "name": "api.alsa.disable-mmap",
            "description": "Disable MMAP",
            "type": {
              "default": false,
              "alt1": false,
              "alt2": true
            },
            "params": true
          },
          {
            "name": "api.alsa.disable-batch",
            "description": "Disable Batch",
            "type": {
              "default": false,
              "alt1": false,
              "alt2": true
            },
            "params": true
          },
          {
            "name": "api.alsa.disable-tsched",
            "description": "Disable timer based scheduling",
            "type": {
              "default": false,
              "alt1": false,
              "alt2": true
            },
            "params": true
          },
          {
            "name": "api.alsa.use-chmap",
            "description": "Use the driver channelmap",
            "type": {
              "default": false,
              "alt1": false,
              "alt2": true
            },
            "params": true
          },
          {
            "name": "api.alsa.multi-rate",
            "description": "Support multiple rates",
            "type": {
              "default": true,
              "alt1": true,
              "alt2": false
            },
            "params": true
          },
          {
            "name": "api.alsa.htimestamp",
            "description": "Use hires timestamps",
            "type": {
              "default": false,
              "alt1": false,
              "alt2": true
            },
            "params": true
          },
          {
            "name": "latency.internal.rate",
            "description": "Internal latency in samples",
            "type": { "default": 0, "min": 0, "max": 65536 },
            "params": true
          },
          {
            "name": "latency.internal.ns",
            "description": "Internal latency in nanoseconds",
            "type": { "default": 0, "min": 0, "max": 2000000000 },
            "params": true
          },
          {
            "name": "clock.name",
            "description": "The name of the clock",
            "type": "api.alsa.p-0",
            "params": true
          },
          {
            "name": "api.alsa.htimestamp.max-errors",
            "description": "Max errors before disabling htimestamp",
            "type": { "default": 64, "min": 0, "max": 2147483647 },
            "params": true
          }
        ],
        "Props": [
          {
            "volume": 1.000000,
            "mute": false,
            "channelVolumes": [ 0.512006, 0.512006, 0.512006, 0.512006 ],
            "channelMap": [ "FL", "FR", "RL", "RR" ],
            "softMute": false,
            "softVolumes": [ 0.512006, 0.512006, 0.512006, 0.512006 ],
            "monitorMute": false,
            "monitorVolumes": [ 1.000000, 1.000000, 1.000000, 1.000000 ],
            "params": [
              "monitor.channel-volumes",
              false,
              "channelmix.disable",
              false,
              "channelmix.min-volume",
              0.000000,
              "channelmix.max-volume",
              10.000000,
              "channelmix.normalize",
              false,
              "channelmix.mix-lfe",
              true,
              "channelmix.upmix",
              true,
              "channelmix.lfe-cutoff",
              0.000000,
              "channelmix.fc-cutoff",
              0.000000,
              "channelmix.rear-delay",
              0.000000,
              "channelmix.stereo-widen",
              0.000000,
              "channelmix.hilbert-taps",
              0,
              "channelmix.upmix-method",
              "none",
              "resample.quality",
              4,
              "resample.disable",
              false,
              "dither.noise",
              0,
              "dither.method",
              "none",
              "debug.wav-path",
              "",
              "channelmix.lock-volumes",
              false,
              "audioconvert.filter-graph.disable",
              false,
              "audioconvert.filter-graph",
              ""
            ]
          },
          {
            "device": "surround40:0",
            "deviceName": "",
            "cardName": "",
            "latencyOffsetNsec": 0,
            "params": [
              "audio.channels",
              4,
              "audio.rate",
              0,
              "audio.format",
              "UNKNOWN",
              "audio.position",
              "[ FL, FR, RL, RR ]",
              "audio.allowed-rates",
              "[  ]",
              "api.alsa.period-size",
              0,
              "api.alsa.period-num",
              0,
              "api.alsa.headroom",
              0,
              "api.alsa.start-delay",
              0,
              "api.alsa.disable-mmap",
              false,
              "api.alsa.disable-batch",
              false,
              "api.alsa.disable-tsched",
              false,
              "api.alsa.use-chmap",
              false,
              "api.alsa.multi-rate",
              true,
              "api.alsa.htimestamp",
              false,
              "api.alsa.htimestamp.max-errors",
              64,
              "latency.internal.rate",
              0,
              "latency.internal.ns",
              0,
              "clock.name",
              "api.alsa.p-0"
            ]
          }
        ],
        "Format": [ ],
        "EnumPortConfig": [
          {
            "direction": "Input",
            "mode": {
              "default": "none",
              "alt1": "none",
              "alt2": "dsp",
              "alt3": "convert"
            },
            "monitor": {
              "default": false,
              "alt1": false,
              "alt2": true
            },
            "control": {
              "default": false,
              "alt1": false,
              "alt2": true
            }
          }
        ],
        "PortConfig": [
          {
            "direction": "Input",
            "mode": "dsp",
            "monitor": true,
            "control": false,
            "format": {
              "mediaType": "audio",
              "mediaSubtype": "raw",
              "format": "F32P",
              "channels": 4,
              "position": [ "FL", "FR", "RL", "RR" ]
            }
          }
        ],
        "Latency": [
          {
            "direction": "Input",
            "minQuantum": 1.000000,
            "maxQuantum": 1.000000,
            "minRate": 0,
            "maxRate": 0,
            "minNs": 0,
            "maxNs": 0
          },
          {
            "direction": "Output",
            "minQuantum": 0.000000,
            "maxQuantum": 0.000000,
            "minRate": 0,
            "maxRate": 0,
            "minNs": 0,
            "maxNs": 0
          }
        ],
        "ProcessLatency": [
          {
            "quantum": 0.000000,
            "rate": 0,
            "ns": 0
          }
        ],
        "Tag": [
          {
            "direction": "Output",
            "info": [
              2,
              "media.name",
              "GullyVR - Watch the VOD on Kick",
              "media.class",
              "Stream/Output/Audio"
            ]
          }
        ]
      }
    }
  },
]

I solved it by creating virtual sinks according to the PipeWire documentation.

My new config file in /etc/pipewire/pipewire.conf.d/scarlett-stereo.conf:

context.modules = [
    {   name = libpipewire-module-loopback
        args = {
            node.description = "Scarlett 4i4 Speakers"
            capture.props = {
                node.name = "Scarlett_4i4_Speakers"
                media.class = "Audio/Sink"
                audio.position = [ FL FR ]
            }
            playback.props = {
                node.name = "playback.Scarlett_4i4_Speakers"
                audio.position = [ AUX0 AUX1 ]
                target.object = "alsa_output.usb-Focusrite_Scarlett_4i4_USB_D8CTQ2Y9A0B20D-00.pro-output-0"
                stream.dont-remix = true
                node.passive = true
            }
        }
    }
    {   name = libpipewire-module-loopback
        args = {
            node.description = "Scarlett 4i4 Headphones"
            capture.props = {
                node.name = "Scarlett_4i4_Headphones"
                media.class = "Audio/Sink"
                audio.position = [ FL FR ]
            }
            playback.props = {
                node.name = "playback.Scarlett_4i4_Headphones"
                audio.position = [ AUX2 AUX3 ]
                target.object = "alsa_output.usb-Focusrite_Scarlett_4i4_USB_D8CTQ2Y9A0B20D-00.pro-output-0"
                stream.dont-remix = true
                node.passive = true
            }
        }
    }
]

For further routing and mixing in the device I’m using alsa-scarlett-gui.