A rather complicated ALSA configuration for NVidia HDMI output (trust me, it has a purpose)

Actually, multiple purposes.  One of them being to workaround the bug in VLC where outputting certain stereo MP3 audio streams via the raw device gets you an alsa audio output error: cannot set buffer duration: Invalid argument.  Additionally, you can control the number of channels any program is outputting (thanks to dmix).  This is incredibly useful if you have a nice receiver capable of doing its own upmixing of various numbers of audio channels.

So, how would you accomplish that with this configuration?

[dlove@skynet ~]$ ALSA_NUM_CHANNELS=2 vlc

From there, you select the Default Alsa Output from VLC. Where does the Default Alsa Output come from? Either /etc/asound.conf or ~/.asoundrc. Here’s the settings I have in mine:

# Use Nvidia HDMI output by default

pcm.!default {
  type plug

  slave.pcm "nvidia-hdmi-output"

  hint {
    show on
    description "Default ALSA Output (currently NVidia HDMI Output)"

  }
}

pcm.nvidia-hdmi-output {
  type asym

  playback.pcm "NVidia.pcm.hdmi.default:CARD=NVidia,DEV=1"
  capture.pcm "hw:C920,0"

  hint {
    show on
    description "NVidia HDMI Output"
  }
}

ctl.!default {
  type hw

  card NVidia
}

The capture part is for recording sound from my webcam. I figured the detail would be useful for anyone trying to accomplish the same thing. You will need to lookup which ALSA capture device you wish to use using arecord -L or arecord -l.

That still leaves /usr/local/share/alsa/pcm/NVidia.conf, the bread and butter of this all. Here it is, in all its glory:

#
# Configuration for the NVIDIA Corporation High Definition Audio Controller
# (e.g. GF110)
#

# default with dmix+softvol
NVidia.pcm.hdmi.default {
  @args [ CARD DEV AES0 AES1 AES2 AES3 FORMAT RATE CHANNELS PERIODSIZE BUFFERSIZE ]
  @args.CARD {
    type string
  }
  @args.DEV {
    type integer
  }
  @args.AES0 {
    type integer
    # consumer, not-copyright, emphasis-none, mode=0
    default 0x04
  }
  @args.AES1 {
    type integer
    default 0x00
  }
  @args.AES2 {
    type integer
    # source and channel
    default 0x00
  }
  @args.AES3 {
    type integer
    default 0x0e
  }
  @args.FORMAT {
    type string
    default "S32_LE"
  }
  @args.RATE {
    type integer
    default {
      @func igetenv
      vars [ ALSA_RATE ]
      default 48000
    }
  }
  @args.CHANNELS {
    type integer
    default {
      @func igetenv
      vars [ ALSA_NUM_CHANNELS ]
      default 8
    }
  }
  @args.PERIODSIZE {
    type integer
    default 16384
  }
  @args.BUFFERSIZE {
    type integer
    default 32768
  }
  type softvol
  slave.pcm {
    @func refer
    name {
      @func concat
      strings [
        "NVidia.pcm.hdmi.route." $CHANNELS ":"
        "DEV=" $DEV ","
        "CARD=" $CARD ","
        "AES0=" $AES0 ","
        "AES1=" $AES1 ","
        "AES2=" $AES2 ","
        "AES3=" $AES3 ","
        "FORMAT=" $FORMAT ","
        "RATE=" $RATE ","
        "PERIODSIZE=" $PERIODSIZE ","
        "BUFFERSIZE=" $BUFFERSIZE
      ]
    }
  }
  control {
    name "PCM Playback Volume"
    card $CARD
  }
}

NVidia.pcm.hdmi.hooks {
  @args [ CARD DEVICE CTLINDEX AES0 AES1 AES2 AES3 FORMAT RATE CHANNELS PERIODSIZE BUFFERSIZE ]
  @args.CARD {
    type string
  }
  @args.DEVICE {
    type integer
  }
  @args.CTLINDEX {
    type integer
  }
  @args.AES0 {
    type integer
  }
  @args.AES1 {
    type integer
  }
  @args.AES2 {
    type integer
  }
  @args.AES3 {
    type integer
  }
  @args.FORMAT {
    type string
  }
  @args.RATE {
    type integer
  }
  @args.CHANNELS {
    type integer
  }
  @args.PERIODSIZE {
    type integer
  }
  @args.BUFFERSIZE {
    type integer
  }
  type hooks
  slave.pcm {
    @func refer
    name {
      @func concat
      strings [
        "NVidia.pcm.hdmi.dmix:"
        "CARD=" $CARD ","
        "DEVICE=" $DEVICE ","
        "FORMAT=" $FORMAT ","
        "RATE=" $RATE ","
        "CHANNELS=" $CHANNELS ","
        "PERIODSIZE=" $PERIODSIZE ","
        "BUFFERSIZE=" $BUFFERSIZE
      ]
    }
  }
  hooks.0 {
    type ctl_elems
    hook_args [
    {
      name "IEC958 Playback Default"
      index $CTLINDEX
      lock true
      preserve true
      value [ $AES0 $AES1 $AES2 $AES3 ]
    }
    {
      name "IEC958 Playback Switch"
      index $CTLINDEX
      value true
    }
    ]
  }
  hint.device $DEVICE
}

NVidia.pcm.hdmi.dmix {
  @args [ CARD DEVICE FORMAT RATE CHANNELS PERIODSIZE BUFFERSIZE ]
  @args.CARD {
    type string
  }
  @args.DEVICE {
    type integer
  }
  @args.FORMAT {
    type string
  }
  @args.RATE {
    type integer
  }
  @args.CHANNELS {
    type integer
  }
  @args.PERIODSIZE {
    type integer
  }
  @args.BUFFERSIZE {
    type integer
  }
  type dmix
  ipc_key {
    @func refer
    name defaults.pcm.ipc_key
  }
  ipc_gid {
    @func refer
    name defaults.pcm.ipc_gid
  }
  ipc_perm {
    @func refer
    name defaults.pcm.ipc_perm
  }
  slave {
    pcm {
      type hw
      card $CARD
      device $DEVICE
    }
    format $FORMAT
    rate $RATE
    channels $CHANNELS
    period_size $PERIODSIZE
    buffer_size $BUFFERSIZE
  }
  slowptr true
}

NVidia.pcm.hdmi.route.8 {
  @args [ CARD DEV AES0 AES1 AES2 AES3 FORMAT RATE PERIODSIZE BUFFERSIZE ]
  @args.CARD { type string }
  @args.DEV { type integer }
  @args.AES0 { type integer }
  @args.AES1 { type integer }
  @args.AES2 { type integer }
  @args.AES3 { type integer }
  @args.FORMAT { type string }
  @args.RATE { type integer }
  @args.PERIODSIZE { type integer }
  @args.BUFFERSIZE { type integer }
  type route
  slave.pcm {
    @func refer
    name {
      @func concat
      strings [
        "NVidia.pcm.hdmi." $DEV ":"
        "CARD=" $CARD ","
        "AES0=" $AES0 ","
        "AES1=" $AES1 ","
        "AES2=" $AES2 ","
        "AES3=" $AES3 ","
        "FORMAT=" $FORMAT ","
        "RATE=" $RATE ","
        "CHANNELS=8,"
        "PERIODSIZE=" $PERIODSIZE ","
        "BUFFERSIZE=" $BUFFERSIZE
      ]
    }
  }
  ttable {
    0.0 1 
    1.1 1  
    2.4 1
    3.5 1
    4.2 1
    5.3 1
    6.6 1
    7.7 1
  }
}

NVidia.pcm.hdmi.route.6 {
  @args [ CARD DEV AES0 AES1 AES2 AES3 FORMAT RATE PERIODSIZE BUFFERSIZE ]
  @args.CARD { type string }
  @args.DEV { type integer }
  @args.AES0 { type integer }
  @args.AES1 { type integer }
  @args.AES2 { type integer }
  @args.AES3 { type integer }
  @args.FORMAT { type string }
  @args.RATE { type integer }
  @args.PERIODSIZE { type integer }
  @args.BUFFERSIZE { type integer }
  type route
  slave.pcm {
    @func refer
    name {
      @func concat
      strings [
        "NVidia.pcm.hdmi." $DEV ":"
        "CARD=" $CARD ","
        "AES0=" $AES0 ","
        "AES1=" $AES1 ","
        "AES2=" $AES2 ","
        "AES3=" $AES3 ","
        "FORMAT=" $FORMAT ","
        "RATE=" $RATE ","
        "CHANNELS=6,"
        "PERIODSIZE=" $PERIODSIZE ","
        "BUFFERSIZE=" $BUFFERSIZE
      ]
    }
  }
  ttable {
    0.0 1 
    1.1 1  
    2.4 1
    3.5 1
    4.2 1
    5.3 1
  }
}

NVidia.pcm.hdmi.route.2 {
  @args [ CARD DEV AES0 AES1 AES2 AES3 FORMAT RATE PERIODSIZE BUFFERSIZE ]
  @args.CARD { type string }
  @args.DEV { type integer }
  @args.AES0 { type integer }
  @args.AES1 { type integer }
  @args.AES2 { type integer }
  @args.AES3 { type integer }
  @args.FORMAT { type string }
  @args.RATE { type integer }
  @args.PERIODSIZE { type integer }
  @args.BUFFERSIZE { type integer }
  type route
  slave.pcm {
    @func refer
    name {
      @func concat
      strings [
        "NVidia.pcm.hdmi." $DEV ":"
        "CARD=" $CARD ","
        "AES0=" $AES0 ","
        "AES1=" $AES1 ","
        "AES2=" $AES2 ","
        "AES3=" $AES3 ","
        "FORMAT=" $FORMAT ","
        "RATE=" $RATE ","
        "CHANNELS=2,"
        "PERIODSIZE=" $PERIODSIZE ","
        "BUFFERSIZE=" $BUFFERSIZE
      ]
    }
  }
  ttable {
    0.0 1 
    1.1 1  
  }
}

NVidia.pcm.hdmi.route.1 {
  @args [ CARD DEV AES0 AES1 AES2 AES3 FORMAT RATE PERIODSIZE BUFFERSIZE ]
  @args.CARD { type string }
  @args.DEV { type integer }
  @args.AES0 { type integer }
  @args.AES1 { type integer }
  @args.AES2 { type integer }
  @args.AES3 { type integer }
  @args.FORMAT { type string }
  @args.RATE { type integer }
  @args.PERIODSIZE { type integer }
  @args.BUFFERSIZE { type integer }
  type route
  slave.pcm {
    @func refer
    name {
      @func concat
      strings [
        "NVidia.pcm.hdmi." $DEV ":"
        "CARD=" $CARD ","
        "AES0=" $AES0 ","
        "AES1=" $AES1 ","
        "AES2=" $AES2 ","
        "AES3=" $AES3 ","
        "FORMAT=" $FORMAT ","
        "RATE=" $RATE ","
        "CHANNELS=1,"
        "PERIODSIZE=" $PERIODSIZE ","
        "BUFFERSIZE=" $BUFFERSIZE
      ]
    }
  }
  ttable {
    0.0 1
  }
}

NVidia.pcm.hdmi.0 {
  @args [ CARD AES0 AES1 AES2 AES3 FORMAT RATE CHANNELS PERIODSIZE BUFFERSIZE ]
  @args.CARD { type string }
  @args.AES0 { type integer }
  @args.AES1 { type integer }
  @args.AES2 { type integer }
  @args.AES3 { type integer }
  @args.FORMAT { type string }
  @args.RATE { type integer }
  @args.CHANNELS { type integer }
  @args.PERIODSIZE { type integer }
  @args.BUFFERSIZE { type integer }
  @func refer
  name {
    @func concat
    strings [
      "NVidia.pcm.hdmi.hooks:"
      "CARD=" $CARD ","
      "DEVICE=3,"
      "CTLINDEX=0,"
      "AES0=" $AES0 ","
      "AES1=" $AES1 ","
      "AES2=" $AES2 ","
      "AES3=" $AES3 ","
      "FORMAT=" $FORMAT ","
      "RATE=" $RATE ","
      "CHANNELS=" $CHANNELS ","
      "PERIODSIZE=" $PERIODSIZE ","
      "BUFFERSIZE=" $BUFFERSIZE
    ]
  }
}

NVidia.pcm.hdmi.1 {
  @args [ CARD AES0 AES1 AES2 AES3 FORMAT RATE CHANNELS PERIODSIZE BUFFERSIZE ]
  @args.CARD { type string }
  @args.AES0 { type integer }
  @args.AES1 { type integer }
  @args.AES2 { type integer }
  @args.AES3 { type integer }
  @args.FORMAT { type string }
  @args.RATE { type integer }
  @args.CHANNELS { type integer }
  @args.PERIODSIZE { type integer }
  @args.BUFFERSIZE { type integer }
  @func refer
  name {
    @func concat
    strings [
      "NVidia.pcm.hdmi.hooks:"
      "CARD=" $CARD ","
      "DEVICE=7,"
      "CTLINDEX=1,"
      "AES0=" $AES0 ","
      "AES1=" $AES1 ","
      "AES2=" $AES2 ","
      "AES3=" $AES3 ","
      "FORMAT=" $FORMAT ","
      "RATE=" $RATE ","
      "CHANNELS=" $CHANNELS ","
      "PERIODSIZE=" $PERIODSIZE ","
      "BUFFERSIZE=" $BUFFERSIZE
    ]
  }
}

NVidia.pcm.hdmi.2 {
  @args [ CARD AES0 AES1 AES2 AES3 FORMAT RATE CHANNELS PERIODSIZE BUFFERSIZE ]
  @args.CARD { type string }
  @args.AES0 { type integer }
  @args.AES1 { type integer }
  @args.AES2 { type integer }
  @args.AES3 { type integer }
  @args.FORMAT { type string }
  @args.RATE { type integer }
  @args.CHANNELS { type integer }
  @args.PERIODSIZE { type integer }
  @args.BUFFERSIZE { type integer }
  @func refer
  name {
    @func concat
    strings [
      "NVidia.pcm.hdmi.hooks:"
      "CARD=" $CARD ","
      "DEVICE=8,"
      "CTLINDEX=2,"
      "AES0=" $AES0 ","
      "AES1=" $AES1 ","
      "AES2=" $AES2 ","
      "AES3=" $AES3 ","
      "FORMAT=" $FORMAT ","
      "RATE=" $RATE ","
      "CHANNELS=" $CHANNELS ","
      "PERIODSIZE=" $PERIODSIZE ","
      "BUFFERSIZE=" $BUFFERSIZE
    ]
  }
}

NVidia.pcm.hdmi.3 {
  @args [ CARD AES0 AES1 AES2 AES3 FORMAT RATE CHANNELS PERIODSIZE BUFFERSIZE ]
  @args.CARD { type string }
  @args.AES0 { type integer }
  @args.AES1 { type integer }
  @args.AES2 { type integer }
  @args.AES3 { type integer }
  @args.FORMAT { type string }
  @args.RATE { type integer }
  @args.CHANNELS { type integer }
  @args.PERIODSIZE { type integer }
  @args.BUFFERSIZE { type integer }
  @func refer
  name {
    @func concat
    strings [
      "NVidia.pcm.hdmi.hooks:"
      "CARD=" $CARD ","
      "DEVICE=9,"
      "CTLINDEX=3,"
      "AES0=" $AES0 ","
      "AES1=" $AES1 ","
      "AES2=" $AES2 ","
      "AES3=" $AES3 ","
      "FORMAT=" $FORMAT ","
      "RATE=" $RATE ","
      "CHANNELS=" $CHANNELS ","
      "PERIODSIZE=" $PERIODSIZE ","
      "BUFFERSIZE=" $BUFFERSIZE
    ]
  }
}

NVidia.pcm.hdmi.4 {
  @args [ CARD AES0 AES1 AES2 AES3 FORMAT RATE CHANNELS PERIODSIZE BUFFERSIZE ]
  @args.CARD { type string }
  @args.AES0 { type integer }
  @args.AES1 { type integer }
  @args.AES2 { type integer }
  @args.AES3 { type integer }
  @args.FORMAT { type string }
  @args.RATE { type integer }
  @args.CHANNELS { type integer }
  @args.PERIODSIZE { type integer }
  @args.BUFFERSIZE { type integer }
  @func refer
  name {
    @func concat
    strings [
      "NVidia.pcm.hdmi.hooks:"
      "CARD=" $CARD ","
      "DEVICE=10,"
      "CTLINDEX=4,"
      "AES0=" $AES0 ","
      "AES1=" $AES1 ","
      "AES2=" $AES2 ","
      "AES3=" $AES3 ","
      "FORMAT=" $FORMAT ","
      "RATE=" $RATE ","
      "CHANNELS=" $CHANNELS ","
      "PERIODSIZE=" $PERIODSIZE ","
      "BUFFERSIZE=" $BUFFERSIZE
    ]
  }
}

NVidia.pcm.hdmi.5 {
  @args [ CARD AES0 AES1 AES2 AES3 FORMAT RATE CHANNELS PERIODSIZE BUFFERSIZE ]
  @args.CARD { type string }
  @args.AES0 { type integer }
  @args.AES1 { type integer }
  @args.AES2 { type integer }
  @args.AES3 { type integer }
  @args.FORMAT { type string }
  @args.RATE { type integer }
  @args.CHANNELS { type integer }
  @args.PERIODSIZE { type integer }
  @args.BUFFERSIZE { type integer }
  @func refer
  name {
    @func concat
    strings [
      "NVidia.pcm.hdmi.hooks:"
      "CARD=" $CARD ","
      "DEVICE=11,"
      "CTLINDEX=5,"
      "AES0=" $AES0 ","
      "AES1=" $AES1 ","
      "AES2=" $AES2 ","
      "AES3=" $AES3 ","
      "FORMAT=" $FORMAT ","
      "RATE=" $RATE ","
      "CHANNELS=" $CHANNELS ","
      "PERIODSIZE=" $PERIODSIZE ","
      "BUFFERSIZE=" $BUFFERSIZE
    ]
  }
}

NVidia.pcm.hdmi.6 {
  @args [ CARD AES0 AES1 AES2 AES3 FORMAT RATE CHANNELS PERIODSIZE BUFFERSIZE ]
  @args.CARD { type string }
  @args.AES0 { type integer }
  @args.AES1 { type integer }
  @args.AES2 { type integer }
  @args.AES3 { type integer }
  @args.FORMAT { type string }
  @args.RATE { type integer }
  @args.CHANNELS { type integer }
  @args.PERIODSIZE { type integer }
  @args.BUFFERSIZE { type integer }
  @func refer
  name {
    @func concat
    strings [
      "NVidia.pcm.hdmi.hooks:"
      "CARD=" $CARD ","
      "DEVICE=12,"
      "CTLINDEX=6,"
      "AES0=" $AES0 ","
      "AES1=" $AES1 ","
      "AES2=" $AES2 ","
      "AES3=" $AES3 ","
      "FORMAT=" $FORMAT ","
      "RATE=" $RATE ","
      "CHANNELS=" $CHANNELS ","
      "PERIODSIZE=" $PERIODSIZE ","
      "BUFFERSIZE=" $BUFFERSIZE
    ]
  }
}

NVidia.pcm.hdmi.7 {
  @args [ CARD AES0 AES1 AES2 AES3 FORMAT RATE CHANNELS PERIODSIZE BUFFERSIZE ]
  @args.CARD { type string }
  @args.AES0 { type integer }
  @args.AES1 { type integer }
  @args.AES2 { type integer }
  @args.AES3 { type integer }
  @args.FORMAT { type string }
  @args.RATE { type integer }
  @args.CHANNELS { type integer }
  @args.PERIODSIZE { type integer }
  @args.BUFFERSIZE { type integer }
  @func refer
  name {
    @func concat
    strings [
      "NVidia.pcm.hdmi.hooks:"
      "CARD=" $CARD ","
      "DEVICE=13,"
      "CTLINDEX=7,"
      "AES0=" $AES0 ","
      "AES1=" $AES1 ","
      "AES2=" $AES2 ","
      "AES3=" $AES3 ","
      "FORMAT=" $FORMAT ","
      "RATE=" $RATE ","
      "CHANNELS=" $CHANNELS ","
      "PERIODSIZE=" $PERIODSIZE ","
      "BUFFERSIZE=" $BUFFERSIZE
    ]
  }
}

As you can see, I tried to go with sensible defaults, but a variety of the specific settings are configurable via arguments in your configuration (and a few via environment variable). You may well wish to adjust PERIODSIZE and BUFFERSIZE depending on your audio needs. I hope this article was useful to you. If you have any questions, feel free to leave them in the comments.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s